pax_global_header00006660000000000000000000000064144261763600014523gustar00rootroot0000000000000052 comment=fb768002d4288590083a476af628e51c3f1d47cd libspng-0.7.4/000077500000000000000000000000001442617636000131715ustar00rootroot00000000000000libspng-0.7.4/.circleci/000077500000000000000000000000001442617636000150245ustar00rootroot00000000000000libspng-0.7.4/.circleci/config.yml000066400000000000000000000007431442617636000170200ustar00rootroot00000000000000# .circleci/config.yml version: 2.1 jobs: test-arm: machine: image: ubuntu-2004:202101-01 resource_class: arm.medium steps: - checkout - run: sudo apt install meson ninja-build - run: meson -Ddev_build=true -Db_sanitize=address build - run: ninja -C build test - run: meson configure -Db_sanitize=none build # Some bugs disappear with AddressSanitizer - run: ninja -C build test workflows: build: jobs: - test-armlibspng-0.7.4/.github/000077500000000000000000000000001442617636000145315ustar00rootroot00000000000000libspng-0.7.4/.github/FUNDING.yml000066400000000000000000000000311442617636000163400ustar00rootroot00000000000000open_collective: libspng libspng-0.7.4/.github/ISSUE_TEMPLATE/000077500000000000000000000000001442617636000167145ustar00rootroot00000000000000libspng-0.7.4/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000013171442617636000214100ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. NOTE: Bugs that can cause a crash or memory leak should not be reported on GitHub! Send an e-mail to contact@libspng.org instead. **To Reproduce** Steps to reproduce the behavior: code snippet, PNG file (if applicable), error message(s). **Expected behavior** A clear and concise description of what you expected to happen. **Platform (please complete the following information):** - Architecture: [e.g. x86-64, ARM64] - OS: [e.g. Windows] - Version [e.g. v0.7.2] **Additional context** Add any other context about the problem here. libspng-0.7.4/.github/workflows/000077500000000000000000000000001442617636000165665ustar00rootroot00000000000000libspng-0.7.4/.github/workflows/ci-fuzz.yml000066400000000000000000000011331442617636000206760ustar00rootroot00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'libspng' dry-run: false - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'libspng' fuzz-seconds: 900 dry-run: false - name: Upload Crash uses: actions/upload-artifact@v1 if: failure() with: name: artifacts path: ./out/artifacts libspng-0.7.4/.github/workflows/codeql-analysis.yml000066400000000000000000000041421442617636000224020ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '23 0 * * 1' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'cpp' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 libspng-0.7.4/.github/workflows/on_PR_meson.yaml000066400000000000000000000107251442617636000216750ustar00rootroot00000000000000name: On PRs - meson on: pull_request concurrency: group: ${{github.workflow}}-${{github.head_ref}} cancel-in-progress: true jobs: Ubuntu: runs-on: ubuntu-20.04 name: Linux-GCC${{matrix.cxx}} strategy: matrix: cxx: ['7', '10'] steps: - uses: actions/checkout@v3 - name: Install packages run: | sudo apt install -y gcc-${{matrix.cxx}} g++-${{matrix.cxx}} python3 -m pip install meson ninja - name: Compile and Test env: CC: gcc-${{matrix.cxx}} CXX: g++-${{matrix.cxx}} run: | meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Ddev_build=true meson compile -C "${{github.workspace}}/build" --verbose meson test -C "${{github.workspace}}/build" --verbose Ubuntu-clang: runs-on: ubuntu-20.04 name: Linux-Clang${{matrix.cxx}} strategy: matrix: cxx: ['7', '12'] steps: - uses: actions/checkout@v3 - name: Install packages run: | sudo apt install -y clang-${{matrix.cxx}} libc++abi-${{matrix.cxx}}-dev libc++-${{matrix.cxx}}-dev lld-${{matrix.cxx}} python3 -m pip install meson ninja - name: Compile and Test env: CC: clang-${{matrix.cxx}} CXX: clang++-${{matrix.cxx}} CXXFLAGS: -stdlib=libc++ CC_LD: lld-${{matrix.cxx}} CXX_LD: lld-${{matrix.cxx}} run: | meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dcpp_std=c++20 -Ddev_build=true meson compile -C "${{github.workspace}}/build" --verbose meson test -C "${{github.workspace}}/build" --verbose VisualStudio: runs-on: windows-latest name: MSVC-${{matrix.cxx}} strategy: matrix: cxx: ['cl', 'clang-cl'] steps: - uses: actions/checkout@v3 - name: Install packages run: | python -m pip install meson ninja - uses: ilammy/msvc-dev-cmd@v1 - name: Compile and Test env: CC: ${{matrix.cxx}} CXX: ${{matrix.cxx}} run: | meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dcpp_std=c++latest -Ddev_build=true meson compile -C "${{github.workspace}}/build" --verbose meson test -C "${{github.workspace}}/build" --verbose MSYS2: runs-on: windows-latest name: MSYS2-${{matrix.platform}} strategy: matrix: platform: ['MINGW32', 'MINGW64', 'UCRT64', 'CLANG32', 'CLANG64'] steps: - uses: actions/checkout@v3 - uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.platform}} pacboy: >- cc:p libpng:p meson:p ninja:p pkgconf:p - name: Compile and Test shell: msys2 {0} run: | meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dcpp_std=c++20 -Ddev_build=true meson compile -C "${{github.workspace}}/build" --verbose meson test -C "${{github.workspace}}/build" --verbose MacOS: runs-on: macos-latest name: macOS steps: - uses: actions/checkout@v3 - name: Install packages run: | python3 -m pip install meson ninja - name: Compile and Test run: | meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dcpp_std=c++20 -Ddev_build=true meson compile -C "${{github.workspace}}/build" --verbose meson test -C "${{github.workspace}}/build" --verbose FreeBSD: runs-on: macos-latest name: FreeBSD steps: - uses: actions/checkout@v3 - name: FreeBSD uses: vmactions/freebsd-vm@v0 with: prepare: | pkg install -y ninja meson pkgconf run: | meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dcpp_std=c++20 -Ddev_build=true meson compile -C "${{github.workspace}}/build" --verbose meson test -C "${{github.workspace}}/build" --verbose OpenBSD: runs-on: macos-latest name: OpenBSD steps: - uses: actions/checkout@v3 - name: OpenBSD uses: vmactions/openbsd-vm@v0 with: prepare: | pkg_add libpng ninja meson pkgconf run: | meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dcpp_std=c++20 -Ddev_build=true meson compile -C "${{github.workspace}}/build" --verbose meson test -C "${{github.workspace}}/build" --verbose libspng-0.7.4/.gitignore000066400000000000000000000002371442617636000151630ustar00rootroot00000000000000/build/ /cbuild/ /clang_build/ /arm_build/ /bench/ /vbuild/ /check_build/ /benchmark_images/ /subprojects/*/ /.vscode/ /.frama-c/ /trash/ /tests/formal/ /site/libspng-0.7.4/.gitlab-ci.yml000066400000000000000000000032261442617636000156300ustar00rootroot00000000000000image: debian:bullseye-slim stages: - test - coverity before_script: - apt update - apt install -y build-essential meson git git-lfs unzip clang-tools clang-tidy libfuzzer-11-dev libubsan1 zlib1g-dev libpng-dev pkg-config gcovr wget tar curl test: stage: test script: - CC=clang-11 CXX=clang++-11 meson -Ddev_build=true -Duse_miniz=true -Db_sanitize=address,undefined -Db_lundef=false --wrap-mode=forcefallback -Doptimization=1 -Doss_fuzz=true clang_build - cd clang_build - ninja scan-build # this uses gcc due to a bug: https://github.com/mesonbuild/meson/issues/5716 - ninja test - meson configure -Duse_miniz=false -Db_sanitize=memory - MSAN_OPTIONS=exitcode=100 ninja test - cd .. - CC=gcc CXX=g++ meson -Ddev_build=true -Db_sanitize=address,undefined -Db_coverage=true gcc_build - cd gcc_build - ninja - meson test info -v - ninja test - ninja coverage-text - cat meson-logs/coverage.txt artifacts: paths: - clang_build/ - gcc_build/ coverity: stage: coverity script: - wget https://scan.coverity.com/download/linux64 --post-data "token=$COVERITY_TOKEN&project=randy408%2Flibspng" -O coverity_tool.tgz - mkdir coverity_tool - tar -xvzf coverity_tool.tgz -C coverity_tool --strip-components=1 - ninja -C gcc_build clean - ./coverity_tool/bin/cov-build --dir cov-int ninja -C gcc_build - tar czvf spng.tgz cov-int - curl --form token=$COVERITY_TOKEN --form email=randy408@protonmail.com --form file=@spng.tgz --form version="master" --form description="libspng" https://scan.coverity.com/builds?project=randy408%2Flibspng - rm spng.tgz - rm -r cov-int libspng-0.7.4/CMakeLists.txt000066400000000000000000000060011442617636000157260ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.12) project(libspng C) set(CMAKE_C_STANDARD 99) set(SPNG_MAJOR 0) set(SPNG_MINOR 7) set(SPNG_REVISION 4) set(SPNG_VERSION ${SPNG_MAJOR}.${SPNG_MINOR}.${SPNG_REVISION}) option(ENABLE_OPT "Enable architecture-specific optimizations" ON) option(SPNG_SHARED "Build shared lib" ON) option(SPNG_STATIC "Build static lib" ON) option(BUILD_EXAMPLES "Build examples" ON) include(GNUInstallDirs) include(CMakePackageConfigHelpers) if(NOT CMAKE_HOST_WIN32) set(MATH_LIBRARY "m") else() set(MATH_LIBRARY "") endif() if(NOT ENABLE_OPT) add_definitions( -DSPNG_DISABLE_OPT=1 ) endif() set(spng_TARGETS "") set(spng_SOURCES spng/spng.c) if(SPNG_SHARED) add_library(spng SHARED ${spng_SOURCES}) list(APPEND spng_TARGETS spng) if(BUILD_EXAMPLES) add_executable(example examples/example.c) target_link_libraries(example PRIVATE spng) endif() endif() if(SPNG_STATIC) add_library(spng_static STATIC ${spng_SOURCES}) target_compile_definitions(spng_static PUBLIC SPNG_STATIC) list(APPEND spng_TARGETS spng_static) endif() find_package(ZLIB REQUIRED) foreach(spng_TARGET ${spng_TARGETS}) target_include_directories(${spng_TARGET} PUBLIC $ $ ) target_link_libraries(${spng_TARGET} PRIVATE ZLIB::ZLIB) target_link_libraries(${spng_TARGET} PRIVATE ${MATH_LIBRARY}) endforeach() set(project_config "${CMAKE_CURRENT_BINARY_DIR}/SPNGConfig.cmake") set(project_version_config "${CMAKE_CURRENT_BINARY_DIR}/SPNGConfigVersion.cmake") set(targets_export_name SPNGTargets) set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/spng") configure_package_config_file(cmake/Config.cmake.in ${project_config} INSTALL_DESTINATION ${config_install_dir} ) write_basic_package_version_file(${project_version_config} VERSION ${SPNG_VERSION} COMPATIBILITY SameMajorVersion ) install( TARGETS ${spng_TARGETS} EXPORT ${targets_export_name} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install(FILES spng/spng.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install( FILES ${project_config} ${project_version_config} DESTINATION ${config_install_dir} ) install( EXPORT ${targets_export_name} NAMESPACE "spng::" DESTINATION ${config_install_dir} ) if(NOT CMAKE_HOST_WIN32 OR CYGWIN OR MINGW) set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix ${CMAKE_INSTALL_PREFIX}) set(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) set(LIBS "-lm") foreach(libname ${spng_TARGETS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/libspng.pc.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/lib${libname}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/lib${libname}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endforeach() endif() libspng-0.7.4/CONTRIBUTING.md000066400000000000000000000021461442617636000154250ustar00rootroot00000000000000# Contributing If you intend to make major changes you should let others know by creating a [new issue](https://github.com/randy408/libspng/issues). # Fork Fork this project on GitHub and clone your repository. ``` git clone https://github.com/username/libspng.git cd libspng git remote add upstream https://github.com/randy408/libspng.git ``` Create a debug build ``` meson -Ddev_build=true --buildtype=debug build cd build ``` Enable ASan/UBSan ``` meson configure -Db_sanitize=address,undefined ``` # Code * Coding style should be consistent with the existing style. * Use C99 standard code. * Code must be free of undefined behavior * Code must conform to the rules of the [CERT C Coding Standard](https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard). # Test Start with running Clang Static Analyzer `ninja scan-build` Run the testuite `ninja test` All tests should pass, some runtime errors reported by ASan/UBSan do not fail a test but are considered bugs, check build/meson-logs/testlog.txt for any errors. # Push Create a merge request once you've pushed your local commits. libspng-0.7.4/LICENSE000066400000000000000000000024751442617636000142060ustar00rootroot00000000000000BSD 2-Clause License Copyright (c) 2018-2023, Randy All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libspng-0.7.4/README.md000066400000000000000000000174751442617636000144660ustar00rootroot00000000000000[![Gitter](https://badges.gitter.im/libspng/community.svg)](https://gitter.im/libspng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Financial Contributors on Open Collective](https://opencollective.com/libspng/all/badge.svg?label=financial+contributors)](https://opencollective.com/libspng) [![GitLab CI](https://gitlab.com/randy408/libspng-ci/badges/master/pipeline.svg)](https://gitlab.com/randy408/libspng-ci/pipelines/latest) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/libspng.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#libspng) [![Coverity](https://scan.coverity.com/projects/15336/badge.svg)](https://scan.coverity.com/projects/randy408-libspng) [![coverage](https://gitlab.com/randy408/libspng-ci/badges/master/coverage.svg)](https://gitlab.com/randy408/libspng-ci/-/graphs/master/charts) [![tag](https://img.shields.io/github/tag-date/randy408/libspng.svg)](https://libspng.org/download) # libspng lib**spng** (**s**imple **png**) is a C library for reading and writing Portable Network Graphics (PNG) format files with a focus on security and ease of use. libspng is an alternative to libpng, the projects are separate and the APIs are not compatible. ## Motivation The goal is to provide a fast PNG library with a simpler API than [libpng](https://github.com/glennrp/libpng/blob/libpng16/png.h), it outperforms the reference implementation in common use cases. ## Performance ![](https://libspng.org/perfx86.png) ## Features | Feature | spng | libpng | stb_image | lodepng | |--------------------------------------|---------|--------------------|-----------|---------| | Decode from stream | ✅ | ✅ | ✅ | ❌ | | Gamma correction | ✅ | ✅ | ❌ | ❌ | | No known security bugs[1] | ✅ | ✅ | ❌ | ✅ | | Progressive image read | ✅ | ✅ | ❌ | ❌ | | Parses all standard chunks | ✅ | ✅ | ❌ | ❌ | | Doesn't require zlib[2] | ✅ | ❌ | ✅ | ✅ | | Encoding | ✅ | ✅ | ✅ | ✅ | | Animated PNG | Planned | ✅[3] | ❌ | ❌ | [1] The project is fuzz tested on [OSS-Fuzz](https://github.com/google/oss-fuzz) and vulnerabilities are fixed before they become public. [2] Building with miniz is [supported](docs/build.md#miniz). [3] With a 3rd party patch ## Getting spng Download the [latest release](https://libspng.org/download) and include `spng.c/spng.h` in your project, you can also build with CMake or Meson, refer to the [documentation](docs/build.md) for details. ## Usage ```c #include /* Create a decoder context */ spng_ctx *ctx = spng_ctx_new(0); /* Set an input buffer */ spng_set_png_buffer(ctx, buf, buf_size); /* Determine output image size */ spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size); /* Decode to 8-bit RGBA */ spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0); /* Free context memory */ spng_ctx_free(ctx); /* Creating an encoder context requires a flag */ spng_ctx *enc = spng_ctx_new(SPNG_CTX_ENCODER); /* Encode to internal buffer managed by the library */ spng_set_option(enc, SPNG_ENCODE_TO_BUFFER, 1); /* Specify image dimensions, PNG format */ struct spng_ihdr ihdr = { .width = width, .height = height, .bit_depth = 8, .color_type = SPNG_COLOR_TYPE_TRUECOLOR_ALPHA }; /* Image will be encoded according to ihdr.color_type, .bit_depth */ spng_set_ihdr(enc, &ihdr); /* SPNG_FMT_PNG is a special value that matches the format in ihdr, SPNG_ENCODE_FINALIZE will finalize the PNG with the end-of-file marker */ spng_encode_image(enc, image, image_size, SPNG_FMT_PNG, SPNG_ENCODE_FINALIZE); /* PNG is written to an internal buffer by default */ void *png = spng_get_png_buffer(enc, &png_size, &error); /* User owns the buffer after a successful call */ free(png); /* Free context memory */ spng_ctx_free(enc); ``` See [example.c](examples/example.c). ## License Code is licensed under the BSD 2-clause "Simplified" License. The project contains optimizations and test images from libpng, these are licensed under the [PNG Reference Library License version 2](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt). ## Security & Testing Code is written according to the rules of the [CERT C Coding Standard](https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard). All integer arithmetic is checked for overflow and all error conditions are handled gracefully. The library is continuously fuzzed by [OSS-Fuzz](https://github.com/google/oss-fuzz), code is also scanned with Clang Static Analyzer, Coverity Scan and PVS-Studio. The test suite consists of over 1000 test cases, 175 [test images](http://www.schaik.com/pngsuite/) are decoded with all possible output format and flag combinations and compared against libpng for [correctness](tests/README.md#Correctness). ## Versioning Releases follow the [semantic versioning](https://semver.org/) scheme with additional guarantees: * Releases from 0.4.0 to 0.8.x are stable * If 1.0.0 will introduce breaking changes then 0.8.x will be maintained as a separate stable branch Currently 1.0.0 is planned to be [compatible](https://github.com/randy408/libspng/issues/3). ## Documentation Online documentation is available at [libspng.org/docs](https://libspng.org/docs). ## Known Issues * Decoding to `SPNG_FMT_G8`, `SPNG_FMT_GA8` and `SPNG_FMT_GA16` output formats is not supported for all PNG color type and bit depth combinations, see documentation for details. * Gamma correction is not implemented for `SPNG_FMT_PNG`, `SPNG_FMT_G8`, `SPNG_FMT_GA8` and `SPNG_FMT_GA16` output formats. ## Supporting spng You can sponsor development through [OpenCollective](https://opencollective.com/libspng/), funds will be used for maintenance and further development according to the [roadmap](https://github.com/randy408/libspng/milestones). ### OpenCollective backers #### Individuals #### Organizations Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/libspng/contribute)] libspng-0.7.4/SECURITY.md000066400000000000000000000007611442617636000147660ustar00rootroot00000000000000# Security Policy ## Supported Versions Currently there is only one release branch. | Version | Supported | |-----------------------|--------------------| | Most recent release | :white_check_mark: | | Any previous releases | :x: | ## Reporting a Vulnerability Please report potential security issues to contact@libspng.org - [GPG key](https://libspng.org/randy-pubkey.asc) Response time may vary, issues are prioritized based on severity and impact. libspng-0.7.4/cmake/000077500000000000000000000000001442617636000142515ustar00rootroot00000000000000libspng-0.7.4/cmake/Config.cmake.in000066400000000000000000000002221442617636000170610ustar00rootroot00000000000000@PACKAGE_INIT@ find_dependency(ZLIB REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") check_required_components(spng) libspng-0.7.4/cmake/libspng.pc.in000066400000000000000000000004101442617636000166330ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/ Name: lib@libname@ Description: PNG decoding and encoding library Version: @SPNG_VERSION@ Requires: zlib Libs: -L${libdir} -l@libname@ Libs.private: @LIBS@ Cflags: -I${includedir} libspng-0.7.4/docs/000077500000000000000000000000001442617636000141215ustar00rootroot00000000000000libspng-0.7.4/docs/api.md000066400000000000000000000003101442617636000152060ustar00rootroot00000000000000# API Documentation * [Error handling](errors.md) * [Versioning](version.md) * [Common](context.md) * [Getting and setting chunk data](chunk.md) * [Decoder API](decode.md) * [Encoder API](encode.md) libspng-0.7.4/docs/build.md000066400000000000000000000070661442617636000155530ustar00rootroot00000000000000# Build ## Platform requirements * Requires [zlib](http://zlib.net) or a zlib-compatible library * Integers must be two's complement. * Fixed width integer types up to `(u)int32_t` * `CHAR_BIT` must equal 8. * `size_t` must be unsigned. * `size_t` and `int` must be at least 32-bit, 16-bit platforms are not supported. * Floating point support and math functions ## CMake ```bash mkdir cbuild cd cbuild cmake .. # Don't forget to set optimization level! make make install ``` ## Meson ```bash meson build --buildtype=release # Default is debug cd build ninja ninja install ``` ## Embedding the source code The source files `spng.c`/`spng.h` can be embedded in a project without any configuration, SSE2 intrinsics are enabled by default on x86. ## Build options | Meson | CMake | Compiler option | Default | Description | |-------------|------------|-----------------------------|---------|----------------------------------------------------| | (auto) | (auto) | `SPNG_STATIC` | | Controls symbol exports on Windows | | enable_opt | ENABLE_OPT | `SPNG_DISABLE_OPT` | ON | Compile with optimizations | | | | `SPNG_SSE=<1-4>` | 1 | SSE version target for x86 (ignored on non-x86) | | | | `SPNG_ARM` | (auto) | Enable ARM NEON optimizations (ARM64 only) | | static_zlib | | | OFF | Link zlib statically | | use_miniz | | `SPNG_USE_MINIZ` | OFF | Compile using miniz, disables some features | | (auto) | | `SPNG_ENABLE_TARGET_CLONES` | | Use target_clones() to optimize (GCC + glibc only) | | dev_build | | | OFF | Enable the testsuite, requires libpng | | benchmarks | | | OFF | Enable benchmarks, requires Git LFS | | oss_fuzz | | | OFF | Enable regression tests with OSS-Fuzz corpora | Valid values for `SPNG_SSE`: * 1 - SSE2 * 2 - same as above * 3 - SSSE3 * 4 - SSE4.1 Currently only SSE2 optimizations are tested. The source code alone can be built without any compiler flags, compiler-specific macros are used to omit the need for options such as `-msse2`, `-mssse3`. ## miniz [miniz](https://github.com/richgel999/miniz) is a single source file replacement for zlib, linking against miniz allows libspng to be embedded into a project with just four files: `spng.c`, `miniz.c` and their headers. For building with miniz add the `SPNG_USE_MINIZ` compiler option, this handles some minor differences in the API. Performance is mostly identical, slightly better in some cases compared to stock zlib. ## Profile-guided optimization [Profile-guided optimization (PGO)](https://clang.llvm.org/docs/UsersManual.html#profile-guided-optimization) improves performance by up to 10%. ```bash # Run in root directory git clone https://github.com/libspng/benchmark_images.git cd build meson configure -Dbuildtype=release --default-library both -Db_pgo=generate ninja ./example ../benchmark_images/medium_rgb8.png ./example ../benchmark_images/medium_rgba8.png ./example ../benchmark_images/large_palette.png meson configure -Db_pgo=use ninja ninja install ``` ## Documentation Documentation is built with [mkdocs](https://www.mkdocs.org/): ```bash # Run in root directory mkdocs build ```libspng-0.7.4/docs/chunk.md000066400000000000000000000170261442617636000155610ustar00rootroot00000000000000# Semantics * Chunk data is stored in `spng_ctx`. * All `spng_get_*()` functions return 0 on success and non-zero error, `SPNG_ECHUNKAVAIL` if the PNG does not contain that chunk or was not previously set. * A successful `spng_set_*()` call will replace any previously set value or list, it does not combine chunk data from the file or multiple `spng_set_*()` calls. The following apply to decoder contexts: * When calling `spng_get_*()` or `spng_set_*()` functions all chunks up to the first IDAT are read, validated then stored with the exception of `spng_get_ihdr()`, which only reads the header. * When calling `spng_get_*()` after the image has been decoded all chunks up to the IEND marker are read. * `spng_set_*()` functions replace stored chunk data for that type. * Chunk data stored with `spng_set_*()` functions are never replaced with input file chunk data i.e. if you set something it will stay that way. # API # spng_get_ihdr() ```c int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr) ``` Get image header # spng_get_plte() ```c int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte) ``` Get image palette # spng_get_trns() ```c int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns) ``` Get image transparency # spng_get_chrm() ```c int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm) ``` Get primary chromacities and white point as floating point numbers # spng_get_chrm_int() ```c int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int) ``` Get primary chromacities and white point in PNG's internal representation # spng_get_gama() ```c int spng_get_gama(spng_ctx *ctx, double *gamma) ``` Get image gamma # spng_get_gama_int() ```c int spng_get_gama_int(spng_ctx *ctx, uint32_t *gamma) ``` Get image gamma in PNG's internal representation # spng_get_iccp() ```c int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp) ``` Get ICC profile !!! note ICC profiles are not validated. # spng_get_sbit() ```c int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit) ``` Get significant bits # spng_get_srgb() ```c int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent) ``` Get rendering intent # spng_get_text() ```c int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text) ``` Copies text information to `text`. `n_text` should be greater than or equal to the number of stored text chunks. If `text` is NULL and `n_text` is non-NULL then `n_text` is set to the number of stored text chunks. Strings may be zero-length (single `'\0'` character) with the exception of `text.keyword`, all strings are guaranteed to be non-NULL. !!! note Due to the structure of PNG files it is recommended to call this function after `spng_decode_image()` to retrieve all text chunks. !!! warning Text data is freed when calling `spng_ctx_free()`. # spng_get_bkgd() ```c int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd) ``` Get image background color # spng_get_hist() ```c int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist) ``` Get image histogram # spng_get_phys() ```c int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys) ``` Get physical pixel dimensions # spng_get_splt() ```c int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt) ``` Copies suggested palettes to `splt`. `n_splt` should be greater than or equal to the number of stored sPLT chunks. If `splt` is NULL and `n_splt` is non-NULL then `n_splt` is set to the number of stored sPLT chunks. !!! warning Suggested palettes are freed when calling `spng_ctx_free()`. # spng_get_time() ```c int spng_get_time(spng_ctx *ctx, struct spng_time *time) ``` Get modification time !!! note Due to the structure of PNG files it is recommended to call this function after `spng_decode_image()`. # spng_get_unknown_chunks() ```c int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks) ``` Copies unknown chunk information to `chunks`. `n_chunks` should be greater than or equal to the number of stored unknown chunks. If `chunks` is NULL and `n_chunks` is non-NULL then `n_chunks` is set to the number of stored chunks. !!! note To retrieve all unknown chunks call this functions after `spng_decode_image()`. !!! warning Chunk data is freed when calling `spng_ctx_free()`. # spng_get_offs() ```c int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs) ``` Get image offset # spng_get_exif() ```c int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif) ``` Get EXIF data !!! note Due to the structure of PNG files it is recommended to call this function after `spng_decode_image()`. !!! warning `exif.data` is freed when calling `spng_ctx_free()`. # spng_set_ihdr() ```c int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr) ``` Set image header # spng_set_plte() ```c int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte) ``` Set image palette # spng_set_trns() ```c int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns) ``` Set transparency # spng_set_chrm() ```c int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm) ``` Set primary chromacities and white point as floating point numbers # spng_set_chrm_int() ```c int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int) ``` Set primary chromacities and white point in PNG's internal representation # spng_set_gama() ```c int spng_set_gama(spng_ctx *ctx, double gamma) ``` Set image gamma # spng_set_gama_int() ```c int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma) ``` Set image gamma in PNG's internal representation # spng_set_iccp() ```c int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp) ``` Set ICC profile `spng_iccp.profile_name` must only contain printable Latin-1 characters and spaces. Leading, trailing, and consecutive spaces are not permitted. !!! note ICC profiles are not validated. # spng_set_sbit() ```c int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit) ``` Set significant bits # spng_set_srgb() ```c int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent) ``` Set rendering intent # spng_set_text() ```c int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text) ``` Set text data `text` should point to an `spng_text` array of `n_text` elements. `spng_text.text` must only contain Latin-1 characters. Newlines must be a single linefeed character (decimal 10). `spng_text.translated_keyword` must not contain linebreaks. `spng_text.compression_method` must be zero. # spng_set_bkgd() ```c int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd) ``` Set image background color # spng_set_hist() ```c int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist) ``` Set image histogram # spng_set_phys() ```c int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys) ``` Set physical pixel dimensions # spng_set_splt() ```c int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt) ``` Set suggested palette(s). `splt` should point to an `spng_splt` array of `n_splt` elements. !!! note `splt` should be a valid reference for the lifetime of the context. # spng_set_time() ```c int spng_set_time(spng_ctx *ctx, struct spng_time *time) ``` Set modification time # spng_set_unknown_chunks() ```c int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks) ``` Set unknown chunk data. !!! note `chunks` should be a valid reference for the lifetime of the context. # spng_set_offs() ```c int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs) ``` Set image offset # spng_set_exif() ```c int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif) ``` Set EXIF data libspng-0.7.4/docs/context.md000066400000000000000000000121661442617636000161350ustar00rootroot00000000000000# Data types # spng_ctx ```c typedef struct spng_ctx spng_ctx; ``` Context handle. !!!note The context handle has no public members. # spng_ctx_flags ```c enum spng_ctx_flags { SPNG_CTX_IGNORE_ADLER32 = 1, /* Ignore checksum in DEFLATE streams */ SPNG_CTX_ENCODER = 2 /* Create an encoder context */ }; ``` # spng_read_fn ```c typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length) ``` Type definition for callback passed to `spng_set_png_stream()` for decoders. A read callback function should copy `length` bytes to `dest` and return 0 or `SPNG_IO_EOF`/`SPNG_IO_ERROR` on error. # spng_write_fn ```c typedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length) ``` Type definition for callback passed to `spng_set_png_stream()` for encoders. The write callback should process `length` bytes and return 0 or `SPNG_IO_ERROR` on error. # spng_format ```c enum spng_format { SPNG_FMT_RGBA8 = 1, SPNG_FMT_RGBA16 = 2, SPNG_FMT_RGB8 = 4, SPNG_FMT_GA8 = 16, SPNG_FMT_GA16 = 32, SPNG_FMT_G8 = 64, /* No conversion or scaling */ SPNG_FMT_PNG = 256, SPNG_FMT_RAW = 512 /* big-endian (everything else is host-endian) */ }; ``` !!! note The channels are always in [byte-order](https://en.wikipedia.org/wiki/RGBA_color_model#RGBA8888) representation. The alpha channel is always [straight alpha](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied), premultiplied alpha is not supported. # spng_filter ```c enum spng_filter { SPNG_FILTER_NONE = 0, SPNG_FILTER_SUB = 1, SPNG_FILTER_UP = 2, SPNG_FILTER_AVERAGE = 3, SPNG_FILTER_PAETH = 4 }; ``` # spng_row_info ```c struct spng_row_info { uint32_t scanline_idx; uint32_t row_num; int pass; uint8_t filter; }; ``` Contains row and scanline information, used for progressive decoding and encoding. # spng_option ```c enum spng_option { SPNG_KEEP_UNKNOWN_CHUNKS = 1, SPNG_IMG_COMPRESSION_LEVEL, SPNG_IMG_WINDOW_BITS, SPNG_IMG_MEM_LEVEL, SPNG_IMG_COMPRESSION_STRATEGY, SPNG_TEXT_COMPRESSION_LEVEL, SPNG_TEXT_WINDOW_BITS, SPNG_TEXT_MEM_LEVEL, SPNG_TEXT_COMPRESSION_STRATEGY, SPNG_FILTER_CHOICE, SPNG_CHUNK_COUNT_LIMIT, SPNG_ENCODE_TO_BUFFER, }; ``` # spng_filter_choice ```c enum spng_filter_choice { SPNG_DISABLE_FILTERING = 0, SPNG_FILTER_CHOICE_NONE = 8, SPNG_FILTER_CHOICE_SUB = 16, SPNG_FILTER_CHOICE_UP = 32, SPNG_FILTER_CHOICE_AVG = 64, SPNG_FILTER_CHOICE_PAETH = 128, SPNG_FILTER_CHOICE_ALL = (8|16|32|64|128) }; ``` # API # spng_ctx_new() ```c spng_ctx *spng_ctx_new(int flags) ``` Creates a new context. # spng_ctx_new2() ```c spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags) ``` Creates a new context with a custom memory allocator, it is passed to zlib. `alloc` and its members must be non-NULL. # spng_ctx_free() ```c void spng_ctx_free(spng_ctx *ctx) ``` Releases context resources. # spng_set_png_stream() ```c int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user) ``` Set input PNG stream or output PNG stream, depending on context type. This can only be done once per context. !!! info PNG's are read up to the file end marker, this is identical behavior to libpng. # spng_set_png_file() ```c int spng_set_png_file(spng_ctx *ctx, FILE *file) ``` Set input PNG file or output PNG file, depending on context type. This can only be done once per context. # spng_set_image_limits() ```c int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height) ``` Set image width and height limits, these may not be larger than 231-1. # spng_get_image_limits() ```c int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height) ``` Get image width and height limits. `width` and `height` must be non-NULL. # spng_set_chunk_limits() ```c int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_limit) ``` Set chunk size and chunk cache limits, the default chunk size limit is 231-1, the default chunk cache limit is `SIZE_MAX`. Reaching either limit while decoding is handled as an out-of-memory error. !!!note This can only be used for limiting memory usage, most standard chunks do not require additional memory and are stored regardless of these limits. # spng_get_chunk_limits() ```c int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_limit) ``` Get chunk size and chunk cache limits. # spng_get_row_info() ```c int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info) ``` Copies the current, to-be-decoded (or to-be-encoded) row's information to `row_info`. # spng_set_option() ```c int spng_set_option(spng_ctx *ctx, enum spng_option option, int value) ``` Set `option` to the specified `value`. For details see [Decode options](decode.md#decode-options) and [Encode options](encode.md#encode-options). # spng_get_option() ```c int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value) ``` Get the value for the specified `option`. For details see [Decode options](decode.md#decode-options) and [Encode options](encode.md#encode-options). libspng-0.7.4/docs/decode.md000066400000000000000000000300011442617636000156600ustar00rootroot00000000000000# Data types ## spng_crc_action ```c enum spng_crc_action { /* Default for critical chunks */ SPNG_CRC_ERROR = 0, /* Discard chunk, invalid for critical chunks, since v0.6.2: default for ancillary chunks */ SPNG_CRC_DISCARD = 1, /* Ignore and don't calculate checksum */ SPNG_CRC_USE = 2 }; ``` ## spng_decode_flags ```c enum spng_decode_flags { SPNG_DECODE_USE_TRNS = 1, /* Deprecated */ SPNG_DECODE_USE_GAMA = 2, /* Deprecated */ SPNG_DECODE_TRNS = 1, /* Apply transparency */ SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */ SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */ }; ``` # Error handling Decoding errors are divided into critical and non-critical errors. See also: [General error handling](errors.md) Critical errors are not recoverable, it should be assumed that decoding has failed completely and any partial image output is invalid, although corrupted PNG's may appear to decode to the same partial image every time this cannot be guaranteed. A critical error will stop any further parsing, invalidate the context and return the relevant error code, most functions check for a valid context state and return `SPNG_EBADSTATE` for subsequent calls to prevent undefined behavior. It is strongly recommended to check all return values. Non-critical errors in a decoding context refers to file corruption issues that can be handled in a deterministic manner either by ignoring checksums or discarding invalid chunks. The image is extracted consistently but may have lost color accuracy, transparency, etc. The default behavior is meant to emulate libpng for compatibility reasons with existing images in the wild, most non-critical errors are ignored. Configuring decoder strictness is currently limited to checksums. * Invalid palette indices are handled as black, opaque pixels * `tEXt`, `zTXt` chunks with non-Latin characters are considered valid * Non-critical chunks are discarded if the: * Chunk CRC is invalid (`SPNG_CRC_DISCARD` is the default for ancillary chunks) * Chunk has an invalid DEFLATE stream, by default this includes Adler-32 checksum errors * Chunk has errors specific to the chunk type: unexpected length, out-of-range values, etc * Critical chunks with either chunk CRC or Adler-32 errors will stop parsing (unless configured otherwise) * Extra trailing image data is silently discarded * No parsing or validation is done past the `IEND` end-of-file marker Truncated PNG's and truncated image data is always handled as a critical error, getting a partial image is possible with progressive decoding but is not guaranteed to work in all cases. The decoder issues read callbacks that can span multiple rows or even the whole image, partial reads are not processed. Some limitations apply, spng will stop decoding if: * An image row is larger than 4 GB * Something causes arithmetic overflow (limited to extreme cases on 32-bit) ## Checksums There are two types of checksums used in PNG's: 32-bit CRC's for chunk data and Adler-32 in DEFLATE streams. Creating a context with the `SPNG_CTX_IGNORE_ADLER32` flag will cause Adler-32 checksums to be ignored by zlib, both in compressed metadata and image data. Note this is only supported with zlib >= 1.2.11 and is not available when compiled against miniz. Chunk CRC handling is configured with `spng_set_crc_action()`, when `SPNG_CRC_USE` is used for either chunk types the Adler-32 checksums in DEFLATE streams will be also ignored. When set for both chunk types it has the same effect as `SPNG_CTX_IGNORE_ADLER32`, this does not apply vice-versa. Currently there are no distinctions made between Adler-32 checksum- and other errors in DEFLATE streams, they're all mapped to the `SPNG_EZLIB` error code. The libpng equivalent of `spng_set_crc_action()` is `png_set_crc_action()`, it implements a subset of its features: | libpng | spng | Notes | |------------------------|--------------------|-------------------------| | `PNG_CRC_ERROR_QUIT` | `SPNG_CRC_ERROR` | Will not abort on error | | `PNG_CRC_WARN_DISCARD` | `SPNG_CRC_DISCARD` | No warning system | | `PNG_CRC_QUIET_USE` | `SPNG_CRC_USE` | | The `SPNG_CTX_IGNORE_ADLER32` context flag has the same effect as `PNG_IGNORE_ADLER32` used with `png_set_option()`. ## Memory usage The library will always allocate a context buffer, if the input PNG is a stream or file it will also create a read buffer. Decoding an image requires at least 2 rows to be kept in memory, 3 if the image is interlaced. Gamma correction requires an additional 128KB for a lookup table if the output format has 16 bits per channel (e.g. `SPNG_FMT_RGBA16`). To limit memory usage for image decoding use `spng_set_image_limits()` to set an image width/height limit. This is the equivalent of `png_set_user_limits()`. Moreover the size calculated by `spng_decoded_image_size()` can be checked against a hard limit before allocating memory for the output image. Chunks of arbitrary length (e.g. text, color profiles) take up additional memory, `spng_set_chunk_limits()` is used to set hard limits on chunk length and overall memory usage. Since v0.7.0 the `SPNG_CHUNK_COUNT_LIMIT` option controls how many chunks can be stored, the default is `1000` and is configurable through [`spng_set_option()`](context.md#spng_set_option), this limit is independent of the chunk cache limit. Note that exceeding any of the chunk limits is handled as an out-of-memory error. ## Decoding untrusted files To decode untrusted files safely it is required to at least: * Set an upper limit on image dimensions with `spng_set_image_limits()`. * Use `spng_decoded_image_size()` to calculate the output image size and check it against a constant limit. * Set a chunk size and chunk cache limit with `spng_set_chunk_limits()` to control memory usage and avoid DoS from decompression bombs. # API See also: [spng_set_png_stream()](context.md#spng_set_png_stream), [spng_set_png_file()](context.md#spng_set_png_file). # spng_set_png_buffer() ```c int spng_set_png_buffer(spng_ctx *ctx, void *buf, size_t size) ``` Set input PNG buffer, this can only be done once per context. # spng_set_crc_action() ```c int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary) ``` Set how chunk CRC errors should be handled for critical and ancillary chunks. # spng_decoded_image_size() ```c int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *out) ``` Calculates decoded image buffer size for the given output format. An input PNG must be set. # spng_decode_chunks() ```c int spng_decode_chunks(spng_ctx *ctx) ``` Decode all chunks before or after the image data (IDAT) stream, depending on the state of the decoder. If the image is decoded this function will read up to the end-of-file (IEND) marker. Calling this function before `spng_decode_image()` is optional. # spng_decode_image() ```c int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags) ``` Decodes the PNG file and writes the image to `out`, the image is converted from the PNG format to the destination format `fmt`. Interlaced images are deinterlaced, 16-bit images are converted to host-endian. `out` must point to a buffer of length `len`. `len` must be equal to or greater than the number calculated with `spng_decoded_image_size()` with the same output format. If the `SPNG_DECODE_PROGRESSIVE` flag is set the decoder will be initialized with `fmt` and `flags` for progressive decoding, the values of `out`, `len` are ignored. The `SPNG_DECODE_TRNS` flag is silently ignored if the PNG does not contain a tRNS chunk or is not applicable for the color type. This function can only be called once per context. ## Supported format, flag combinations | PNG Format | Output format | Flags | Notes | |--------------|-------------------|--------|-----------------------------------------------| | Any format* | `SPNG_FMT_RGBA8` | All | Convert from any PNG format and bit depth | | Any format | `SPNG_FMT_RGBA16` | All | Convert from any PNG format and bit depth | | Any format | `SPNG_FMT_RGB8` | All | Convert from any PNG format and bit depth | | Gray <=8-bit | `SPNG_FMT_G8` | None** | Only valid for 1, 2, 4, 8-bit grayscale PNG's | | Gray 16-bit | `SPNG_FMT_GA16` | All** | Only valid for 16-bit grayscale PNG's | | Gray <=8-bit | `SPNG_FMT_GA8` | All** | Only valid for 1, 2, 4, 8-bit grayscale PNG's | | Any format | `SPNG_FMT_PNG` | None** | The PNG's format in host-endian | | Any format | `SPNG_FMT_RAW` | None | The PNG's format in big-endian | \* Any combination of color type and bit depth defined in the [standard](https://www.w3.org/TR/2003/REC-PNG-20031110/#table111). \*\* Gamma correction is not implemented The `SPNG_DECODE_PROGRESSIVE` flag is supported in all cases. The alpha channel is always [straight alpha](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied), premultiplied alpha is not supported. ## Progressive image decoding If the `SPNG_DECODE_PROGRESSIVE` flag is set the decoder will be initialized with `fmt` and `flags` for progressive decoding, the values of `img`, `len` are ignored. Progressive decoding is straightforward when the image is not interlaced, calling [spng_decode_row()](#spng_decode_row) for each row of the image will yield the return value `SPNG_EOI` for the final row: ```c int error; size_t image_width = image_size / ihdr.height; for(i = 0; i < ihdr.height; i++) { void *row = image + image_width * i; error = spng_decode_row(ctx, row, image_width); if(error) break; } if(error == SPNG_EOI) /* success */ ``` But for interlaced images rows are accessed multiple times and non-sequentially, use [spng_get_row_info()](context.md#spng_get_row_info) to get the current row number: ```c int error; struct spng_row_info row_info; do { error = spng_get_row_info(ctx, &row_info); if(error) break; void *row = image + image_width * row_info.row_num; error = spng_decode_row(ctx, row, len); } while(!error) if(error == SPNG_EOI) /* success */ ``` This is the recommended solution in all cases, for non-interlaced images `row_num` will increase linearly. # spng_decode_scanline() ```c int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len) ``` Decodes a scanline to `out`. This function requires the decoder to be initialized by calling `spng_decode_image()` with the `SPNG_DECODE_PROGRESSIVE` flag set. The widest scanline is the decoded image size divided by `ihdr.height`. For the last scanline and subsequent calls the return value is `SPNG_EOI`. # spng_decode_row() ```c int spng_decode_row(spng_ctx *ctx, void *out, size_t len) ``` Decodes and deinterlaces a scanline to `out`. This function requires the decoder to be initialized by calling `spng_decode_image()` with the `SPNG_DECODE_PROGRESSIVE` flag set. The width of the row is the decoded image size divided by `ihdr.height`. For interlaced images rows are accessed multiple times and non-sequentially, use [spng_get_row_info()](context.md#spng_get_row_info) to get the current row number. For the last row and subsequent calls the return value is `SPNG_EOI`. If the image is not interlaced this function's behavior is identical to `spng_decode_scanline()`. # Decode options | Option | Default value | Description | |------------------------------|---------------|----------------------------------------------------------| | `SPNG_KEEP_UNKNOWN_CHUNKS` | `0` | Set to keep or discard unknown chunks | | `SPNG_IMG_COMPRESSION_LEVEL` | `-1` | May expose an estimate (0-9) after `spng_decode_image()` | | `SPNG_IMG_WINDOW_BITS` | `15`* | Set zlib window bits used for image decompression | | `SPNG_CHUNK_COUNT_LIMIT` | `1000` | Limit shared by both known and unknown chunks | \* Option may be optimized if not set explicitly. Options not listed here have no effect on decoders. libspng-0.7.4/docs/encode.md000066400000000000000000000222311442617636000157000ustar00rootroot00000000000000# Usage The context must be created with the `SPNG_CTX_ENCODER` flag set. Before any (implicit) write operation an output must be set with [spng_set_png_stream()](context.md#spng_set_png_stream) or [spng_set_png_file()](context.md#spng_set_png_file). Alternatively the `SPNG_ENCODE_TO_BUFFER` option can be enabled with [spng_set_option()](context.md#spng_set_option), in this case the encoder will create and manage an internal output buffer, it is freed by `spng_ctx_free()` unless it's retrieved with [`spng_get_png_buffer()`](#spng_get_png_buffer). In all cases the PNG must be explicitly finalized, this can be done with the `SPNG_ENCODE_FINALIZE` flag or with a call to [`spng_encode_chunks()`](#spng_encode_chunks) after the image has been encoded. # Memory usage On top of the overhead of the the context buffer the internal write buffer may grow to the length of entire chunks or more than the length of the PNG file when encoding to the internal buffer. Encoding an image requires at least 2 rows to be kept in memory, this may increase to 3 rows for future versions. # Data types ## spng_encode_flags ```c enum spng_encode_flags { SPNG_ENCODE_PROGRESSIVE = 1, /* Initialize for progressive writes */ SPNG_ENCODE_FINALIZE = 2, /* Finalize PNG after encoding image */ }; ``` # API # spng_encode_chunks() ```c int spng_encode_chunks(spng_ctx *ctx) ``` Encode all stored chunks before or after the image data (IDAT) stream, depending on the state of the encoder. If the image is encoded this function will also finalize the PNG with the end-of-file (IEND) marker. Calling this function before `spng_encode_image()` is optional. # spng_encode_image() ```c int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags) ``` Encodes the image from `img` in the source format `fmt` to the specified PNG format set with [spng_set_ihdr()](chunk.md#spng_set_ihdr). The width, height, color type, bit depth and interlace method must be set with [spng_set_ihdr()](chunk.md#spng_set_ihdr). If the color type is set to `SPNG_COLOR_TYPE_INDEXED` a palette must be set with [spng_set_plte()](chunk.md#spng_set_plte). `img` must point to a buffer of length `len`, `len` must be equal to the expected image size for the given format `fmt`. This function may call `spng_encode_chunks()`, writing any pending chunks before the image data. If the `SPNG_ENCODE_FINALIZE` flag is set this function will call `spng_encode_chunks()` on the last scanline/row, writing any pending chunks after the image data and finalize the PNG with the end-of-file (IEND) marker before returning. In most cases the only data after the image is the 12-byte IEND marker. When the image is encoded to an internal buffer and the PNG is finalized [spng_get_png_buffer()](#spng_get_png_buffer) will return the encoded buffer, this must be freed by the user. If this function isn't called or an error is encountered the internal buffer is freed by [spng_ctx_free()](context.md#spng_ctx_free). ## Supported format, flag combinations | Input format | PNG Format | Flags | Notes | |----------------|-------------|-------|----------------------------------------| | `SPNG_FMT_PNG` | Any format* | All | Converted from host-endian if required | | `SPNG_FMT_RAW` | Any format* | All | No conversion (assumes big-endian) | \* Any combination of color type and bit depth defined in the [standard](https://www.w3.org/TR/2003/REC-PNG-20031110/#table111). 16-bit images are assumed to be host-endian except for `SPNG_FMT_RAW`. The alpha channel is always [straight alpha](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied), premultiplied alpha is not supported. Compression level and other options can be customized with [`spng_set_option()`](context.md#spng_set_option), see [Encode options](encode.md#encode-options) for all options. Note that encoder options are optimized based on PNG format and compression level, overriding other options such as filtering may disable some of these optimizations. ## Progressive image encoding If the `SPNG_ENCODE_PROGRESSIVE` flag is set the encoder will be initialized with `fmt` and `flags` for progressive encoding, the values of `img`, `len` are ignored. With the `SPNG_ENCODE_FINALIZE` flag set the PNG is finalized on the last scanline or row. Progressive encoding is straightforward when the image is not interlaced, calling [spng_encode_row()](#spng_encode_row) for each row of the image will yield the return value `SPNG_EOI` for the final row: ```c int error; size_t image_width = image_size / ihdr.height; for(i = 0; i < ihdr.height; i++) { void *row = image + image_width * i; error = spng_encode_row(ctx, row, image_width); if(error) break; } if(error == SPNG_EOI) /* success */ ``` But for interlaced images (`spng_ihdr.interlaced_method` set to `1`) rows are accessed multiple times and non-sequentially, use [spng_get_row_info()](context.md#spng_get_row_info) to get the current row number: ```c int error; struct spng_row_info row_info; do { error = spng_get_row_info(ctx, &row_info); if(error) break; void *row = image + image_width * row_info.row_num; error = spng_encode_row(ctx, row, len); } while(!error) if(error == SPNG_EOI) /* success */ ``` # spng_encode_scanline() ```c int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len) ``` Encodes `scanline`, this function is meant to be used for interlaced PNG's with image data that is already split into multiple passes. This function requires the encoder to be initialized by calling `spng_encode_image()` with the `SPNG_ENCODE_PROGRESSIVE` flag set. For the last scanline and subsequent calls the return value is `SPNG_EOI`. # spng_encode_row() ```c int spng_encode_row(spng_ctx *ctx, const void *row, size_t len) ``` Encodes `row`, interlacing it if necessary. This function requires the encoder to be initialized by calling `spng_encode_image()` with the `SPNG_ENCODE_PROGRESSIVE` flag set. See also: [Progressive-image-encoding](#Progressive-image-encoding) For the last row and subsequent calls the return value is `SPNG_EOI`. If the image is not interlaced this function's behavior is identical to `spng_encode_scanline()`. # spng_get_png_buffer() ```c void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error) ``` If `SPNG_ENCODE_TO_BUFFER` is enabled via [spng_set_option()](context.md#spng_set_option) the encoded buffer is returned, it must be called after [spng_encode_image()](encode.md#spng_encode_image) and the PNG must be finalized. On success the buffer must be freed by the user. # Encode options | Option | Default value | Description | |----------------------------------|---------------------------|-----------------------------------| | `SPNG_IMG_COMPRESSION_LEVEL` | `Z_DEFAULT_COMPRESSION` | Set image compression level (0-9) | | `SPNG_IMG_WINDOW_BITS` | `15`* | Set image zlib window bits (9-15) | | `SPNG_IMG_MEM_LEVEL` | `8` | Set zlib `memLevel` for images | | `SPNG_IMG_COMPRESSION_STRATEGY` | `Z_FILTERED`* | Set image compression strategy | | `SPNG_TEXT_COMPRESSION_LEVEL` | `Z_DEFAULT_COMPRESSION` | Set text compression level (0-9) | | `SPNG_TEXT_WINDOW_BITS` | `15` | Set text zlib window bits (9-15) | | `SPNG_TEXT_MEM_LEVEL` | `8` | Set zlib `memLevel` for text | | `SPNG_TEXT_COMPRESSION_STRATEGY` | `Z_DEFAULT_STRATEGY` | Set text compression strategy | | `SPNG_FILTER_CHOICE` | `SPNG_FILTER_CHOICE_ALL`* | Configure or disable filtering | | `SPNG_ENCODE_TO_BUFFER` | `0` | Encode to internal buffer | \* Option may be optimized if not set explicitly. Options not listed here have no effect on encoders. # Performance The default encoder settings match the [reference implementation](http://libpng.org/pub/png/libpng.html) and will produce files of the exact same size, but there are settings that offer better performance for a small increase in file size. Reducing compression level (set `SPNG_IMG_COMPRESSION_LEVEL` to a value lower than `6`) will reduce encoding time for all image types. * Truecolor images - Reduce compression level (between `1` and `5`) and leave everything else on defaults, file size should not increase by more than 10% but encode up to three times faster. * Indexed-color - Choose a reduced compression level (`1`-`5`), file size should stay within 5% but encode up to two times faster. * Grayscale - Choose a reduced compression level (`1`-`5`), file size should stay within 5% but encode up to two times faster. * Grayscale-alpha images - Choose a reduced compression level (`1`-`5`), file size should stay within 5% but encode up to four times faster. * Disabling filtering may further improve performance but will significantly increase file size. !!! note See [encode experiments](https://github.com/libspng/spngt/blob/master/results/README.md#encode-experiments) for more details. Recommendations are based on benchmarks with zlib, performance with other implementations (miniz, zlib-ng) was not measured. libspng-0.7.4/docs/errors.md000066400000000000000000000006671442617636000157700ustar00rootroot00000000000000# Error handling All functions return zero on success and non-zero on error. Some errors such as integer overflow, OOM, decoding errors may lead to a non-recoverable state, in this case all subsequent function calls will return `SPNG_EBADSTATE`. See also: [Decoder error handling](decode.md#error-handling) # Functions # spng_strerror() ```c const char *spng_strerror(int err) ``` Return the error message for the given error code. libspng-0.7.4/docs/index.md000066400000000000000000000006001442617636000155460ustar00rootroot00000000000000# libspng documentation ## Overview libspng is a C library for reading and writing Portable Network Graphics (PNG) format files with a focus on security and ease of use. ## Documentation * [Build](build.md) * [Usage](usage.md) * [API documentation](api.md) * [libpng migration guide](migrate-libpng.md) ## Download libspng can be downloaded [here](https://libspng.org/download).libspng-0.7.4/docs/migrate-libpng.md000066400000000000000000000714311442617636000173520ustar00rootroot00000000000000# Contexts The context handle `spng_ctx` is an opaque datatype that holds all information, there is no separate info struct. Create a new context with [`spng_ctx_new()`](context.md#spng_ctx_new) or [`spng_ctx_new2()`](context.md#spng_ctx_new2) to set a custom memory allocator. Contexts are decoders by default, to create an encoder use `SPNG_CTX_ENCODER` as context flag. `spng_ctx_free()` frees all context data. # Error handling All functions return zero on success and non-zero on error, decoding or encoding errors will invalidate the context and most subsequent function calls will return `SPNG_EBADSTATE` to signal this. See also: [Decoder error handling](decode.md#error-handling) # Limits !!! note `cache_max` refers to an overall memory usage limit in spng. | libpng | spng | Notes | |------------------------------|-----------------------------------------------------|--------------------------| | `png_set_user_limits()` | [`spng_set_image_limits()`](#spng_set_image_limits) | | | `png_get_user_width_max()` | [`spng_get_image_limits()`](#spng_get_image_limits) | | | `png_get_user_height_max()` | [`spng_get_image_limits()`](#spng_get_image_limits) | | | `png_set_chunk_malloc_max()` | [`spng_set_chunk_limits()`](#spng_set_chunk_limits) | `chunk_size` argument | | `png_get_chunk_malloc_max()` | [`spng_get_chunk_limits()`](#spng_get_chunk_limits) | `chunk_size` argument | | `png_set_chunk_cache_max()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_CHUNK_COUNT_LIMIT` | # Image information and chunk data !!! info Image dimensions and other basic properties can be retrieved with [`spng_get_ihdr()`](chunk.md#spng_get_ihdr). !!! note Most chunk data is copied except for arbitrary length chunks (eXIf, text, sPLT, unknown chunks). Chunk lists are not copied, the reference must stay valid for the lifetime of the context. To retrieve chunk lists for text, sPLT and unknown chunks the user must allocate buffers for them after querying the list size, unlike with libpng the internal list pointers are not accessible. | libpng | spng | Notes | |------------------------------|-----------------------------------------------------------------|-----------------------| | `png_get_IHDR()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | Get image information | | `png_get_PLTE()` | [`spng_get_plte()`](chunk.md#spng_get_plte) | | | `png_get_tRNS()` | [`spng_get_trns()`](chunk.md#spng_get_trns) | | | `png_get_cHRM()` | [`spng_get_chrm()`](chunk.md#spng_get_chrm) | | | `png_get_cHRM_fixed()` | [`spng_get_chrm_int()`](chunk.md#spng_get_chrm_int) | | | `png_get_gAMA()` | [`spng_get_gama()`](chunk.md#spng_get_gama) | | | `png_get_gAMA_fixed()` | [`spng_get_gama_int()`](chunk.md#spng_get_gama_int) | | | `png_get_iCCP()` | [`spng_get_iccp()`](chunk.md#spng_get_iccp) | | | `png_get_sBIT()` | [`spng_get_sbit()`](chunk.md#spng_get_sbit) | | | `png_get_sRGB()` | [`spng_get_srgb()`](chunk.md#spng_get_srgb) | | | `png_get_text()` | [`spng_get_text()`](chunk.md#spng_get_text) | | | `png_get_bKGD()` | [`spng_get_bkgd()`](chunk.md#spng_get_bkgd) | | | `png_get_hIST()` | [`spng_get_hist()`](chunk.md#spng_get_hist) | | | `png_get_pHYs()` | [`spng_get_phys()`](chunk.md#spng_get_phys) | | | `png_get_sPLT()` | [`spng_get_splt()`](chunk.md#spng_get_splt) | | | `png_get_tIME()` | [`spng_get_time()`](chunk.md#spng_get_time) | | | `png_get_oFFs()` | [`spng_get_offs()`](chunk.md#spng_get_offs) | | | `png_get_eXIf_1()` | [`spng_get_exif()`](chunk.md#spng_get_exif) | | | `png_get_sCAL())` | N/A | Not supported | | `png_set_IHDR()` | [`spng_set_ihdr()`](chunk.md#spng_set_ihdr) | | | `png_set_PLTE()` | [`spng_set_plte()`](chunk.md#spng_set_plte) | | | `png_set_tRNS()` | [`spng_set_trns()`](chunk.md#spng_set_trns) | | | `png_set_cHRM()` | [`spng_set_chrm()`](chunk.md#spng_set_chrm) | | | `png_set_cHRM_fixed()` | [`spng_set_chrm_int()`](chunk.md#spng_set_chrm_int) | | | `png_set_gAMA()` | [`spng_set_gama()`](chunk.md#spng_set_gama) | | | `png_set_gAMA_fixed()` | [`spng_set_gama_int()`](chunk.md#spng_set_gama_int) | | | `png_set_iCCP()` | [`spng_set_iccp()`](chunk.md#spng_set_iccp) | | | `png_set_sBIT()` | [`spng_set_sbit()`](chunk.md#spng_set_sbit) | | | `png_set_sRGB()` | [`spng_set_srgb()`](chunk.md#spng_set_srgb) | | | `png_set_text()` | [`spng_set_text()`](chunk.md#spng_set_text) | | | `png_set_bKGD()` | [`spng_set_bkgd()`](chunk.md#spng_set_bkgd) | | | `png_set_hIST()` | [`spng_set_hist()`](chunk.md#spng_set_hist) | | | `png_set_pHYs()` | [`spng_set_phys()`](chunk.md#spng_set_phys) | | | `png_set_sPLT()` | [`spng_set_splt()`](chunk.md#spng_set_splt) | | | `png_set_tIME()` | [`spng_set_time()`](chunk.md#spng_set_time) | | | `png_set_oFFs()` | [`spng_set_offs()`](chunk.md#spng_set_offs) | | | `png_set_eXIf_1()` | [`spng_set_exif()`](chunk.md#spng_set_exif) | | | `png_set_sCAL())` | N/A | Not supported | | `png_get_unknown_chunks()` | [`spng_get_unknown_chunks()`](chunk.md#spng_get_unknown_chunks) | | | `png_set_unknown_chunks()` | [`spng_set_unknown_chunks()`](chunk.md#spng_set_unknown_chunks) | | | `png_get_image_width()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | | | `png_get_image_height()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | | | `png_get_bit_depth()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | | | `png_get_color_type()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | | | `png_get_filter_type()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | | | `png_get_interlace_type()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | | | `png_get_compression_type()` | [`spng_get_ihdr()`](chunk.md#spng_get_ihdr) | | # Decode ## Set source PNG !!! note Push-style decoding is not supported in this release. | libpng | spng | Notes | |---------------------|-----------------------------------------------------------|-------------------------| | `png_set_read_fn()` | [`spng_set_png_stream()`](context.md#spng_set_png_stream) | Set PNG stream callback | | `png_init_io()` | [`spng_set_png_file()`](context.md#spng_set_png_file) | Set PNG file (`FILE*`) | | N/A | [`spng_set_png_buffer()`](context.md#spng_set_png_buffer) | Set PNG buffer | ## Decoder configuration | libpng | spng | Notes | |--------------------------------|----------------------------------------------------------|-------------------------------------------------------------------------| | `png_set_crc_action()` | [`spng_set_crc_action()`](decode.md#spng_set_crc_action) | See [CRC actions](#crc-action-constants) | | `png_set_interlace_handling()` | N/A | See [Progressive image decoding](decode.md#progressive-image-decoding). | | `png_create_read_struct()` | N/A | See [Contexts](#contexts) | | `png_destroy_read_struct()` | N/A | See [Contexts](#contexts) | ### CRC action constants !!! note Errors are only signalled through return values, spng never calls `abort()`. | libpng | spng | Action - critical | Action - ancillary | |------------------------|--------------------|---------------------|---------------------| | `PNG_CRC_DEFAULT` | N/A | `SPNG_CRC_ERROR` | `SPNG_CRC_USE` | | `PNG_CRC_ERROR_QUIT` | `SPNG_CRC_ERROR` | error | error | | `PNG_CRC_WARN_DISCARD` | `SPNG_CRC_DISCARD` | (invalid) | discard data | | `PNG_CRC_WARN_USE` | N/A | (no warning system) | (no warning system) | | `PNG_CRC_QUIET_USE` | `SPNG_CRC_USE` | use data | use data | | `PNG_CRC_NO_CHANGE` | N/A | | | ## Image formats and transforms Currently the library only works with source and destination formats, there are no transforms which change the output format based on the source PNG's format or other metadata like the tRNS chunk. Migrating from the traditional libpng API is straightforward if the desired output format is clearly defined, nonetheless some transforms (such as [Adding transparency](#adding-transparency)) can be implemented on top of the existing API. All [`spng_format`](context.md#spng_format)'s are explicit and host-endian (except for `SPNG_FMT_PNG` and `SPNG_FMT_RAW`), PNG formats are converted to the destination `spng_format` when decoding or converted from the `spng_format` to the destination PNG format when encoding. When the destination format has an alpha channel and the source doesn't the alpha samples are implicitly set to the fully opaque, maximum value. The alpha channel is always [straight alpha](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied), premultiplied alpha is not supported. !!! note Some source/destination combinations are not supported, check supported format, flags combinations for [decoding](decode.md#supported-format-flag-combinations) and [encoding](encode.md#supported-format-flag-combinations). `SPNG_FMT_PNG` represents the PNG's format specified in the header (IHDR chunk) in host-endian (ie. little-endian on x86), it is the equivalent of only calling `png_set_swap()` on little-endian when decoding or encoding. When decoding the output will always be host-endian, when encoding it is assumed the source image is host-endian. `SPNG_FMT_RAW` is the same as `SPNG_FMT_PNG` but in big-endian, it is the same as not doing any transformations when decoding or encoding with libpng. The PNG standard only supports encoding of 16-bit images in big-endian, when used as the source format the library will assume the source image is big-endian. When decoding 16-bit images the output will always be big-endian. Some decode flags may affect the final image but never the size or the layout of the samples within the pixels. ## Adding transparency This can be achieved by specifying an output format with an alpha channel such as `SPNG_FMT_RGBA8` and the `SPNG_DECODE_TRNS` decode flag. ```c ret = spng_decode_image(ctx, out, len, SPNG_FMT_RGBA8, SPNG_DECODE_TRNS); ``` Note that using `SPNG_DECODE_TRNS` does not result in an error if the image does not have a tRNS chunk or is not applicable for the PNG format/output format combination, in those cases the flag is ignored. The `png_set_tRNS_to_alpha()` function applies a transform which adds an alpha channel of the same bit depth if a tRNS chunk is present. It also implicitly converts indexed color images to 8-bit RGB, 1/2/4-bit grayscale images to 8-bit grayscale and also adds an alpha channel if there is a tRNS chunk. This transform can be implemented by choosing the equivalent output format and using `SPNG_DECODE_TRNS` to apply transparency: ```c int fmt = SPNG_FMT_PNG; int decode_flags = SPNG_DECODE_TRNS; if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) fmt = SPNG_FMT_RGB8; else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr.bit_depth < 8) fmt = SPNG_FMT_G8; struct spng_trns trns = {0}; int have_trns = !spng_get_trns(ctx, &trns); if(have_trns) { if(ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) { if(ihdr.bit_depth == 16) fmt = SPNG_FMT_RGBA16; else fmt = SPNG_FMT_RGBA8; } else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) { if(ihdr.bit_depth == 16) fmt = SPNG_FMT_GA16; else fmt = SPNG_FMT_GA8; } else if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) { fmt = SPNG_FMT_RGBA8; } } ``` | libpng | spng | Notes | |------------------------------------|--------------------|-----------------------------------------------------------| | `png_set_swap()` | N/A | Byteswapping is done based on source / destination format | | `png_set_expand_gray_1_2_4_to_8()` | N/A | Use `SPNG_FMT_G8` when required | | `png_set_tRNS_to_alpha()` | N/A | See [Adding transparency](#Adding-transparency) | | `png_set_palette_to_rgb()` | N/A | Use `SPNG_FMT_RGB8` when required | | `png_set_gamma()` | `SPNG_DECODE_GAMA` | Screen gamma of 2.2 is assumed | | `png_read_update_info()` | N/A | Not required | | `png_set_expand_16()` | N/A | Not supported | | `png_set_bgr()` | N/A | Not supported | | `png_set_gray_to_rgb()` | N/A | Use `SPNG_FMT_RGB8` when required | | `png_set_rgb_to_gray()` | N/A | Not supported | | `png_set_rgb_to_gray_fixed()` | N/A | Not supported | | `png_build_grayscale_palette()` | N/A | Not supported | | `png_set_alpha_mode()` | N/A | Not supported | | `png_set_alpha_mode_fixed()` | N/A | Not supported | | `png_set_strip_alpha()` | N/A | Not supported | | `png_set_swap_alpha()` | N/A | Not supported | | `png_set_invert_alpha()` | N/A | Not supported | | `png_set_filler()` | N/A | Not supported | | `png_set_add_alpha()` | N/A | Not supported | | `png_set_swap_alpha()` | N/A | Not supported | | `png_set_packing()` | N/A | Not supported | | `png_set_packswap()` | N/A | Not supported | | `png_set_shift()` | N/A | Not supported | | `png_set_invert_mono()` | N/A | Not supported | | `png_set_background()` | N/A | Not supported | | `png_set_background_fixed()` | N/A | Not supported | | `png_set_scale_16()` | N/A | Not supported | | `png_set_strip_16()` | N/A | Not supported | # Decoding images See also: [Progressive image decoding](decode.md#progressive-image-decoding) !!! note Row pointers (array of pointers) are not used. | libpng | spng | Notes | |---------------------|--------------------------------------------------------|-------------------------------------| | `png_read_row()` | [`spng_decode_row()`](decode.md#spng_decode_row) | | | `png_read_image()` | [`spng_decode_image()`](decode.md#spng_decode_image) | | | `png_read_info()` | [`spng_decode_chunks()`](decode.md#spng_decode_chunks) | Optional, chunks are read on-demand | | `png_read_end()` | [`spng_decode_chunks()`](decode.md#spng_decode_chunks) | Optional, chunks are read on-demand | # Encode ## Set destination PNG | libpng | spng | Notes | |----------------------|-----------------------------------------------------------------------------|---------------------------| | `png_set_write_fn()` | [`spng_set_png_stream()`](context.md#spng_set_png_stream) | Set PNG stream callback | | `png_init_io()` | [`spng_set_png_file()`](decode.md#spng_set_png_file) | Set PNG file (`FILE*`) | | N/A | [`spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER)`](context.md#spng_set_option) | Encode to internal buffer | ## Encoder configuration | libpng | spng | Notes | |------------------------------------------|-----------------------------------------------------|--------------------------------------------------------------| | `png_create_write_struct()` | N/A | See [Contexts](#contexts) | | `png_destroy_write_struct()` | N/A | See [Contexts](#contexts) | | `png_set_keep_unknown_chunks()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_KEEP_UNKNOWN_CHUNKS` | | `png_handle_as_unknown()` | N/A | Not supported | | `png_set_unknown_chunk_location()` | N/A | Not supported, set `location` field ahead of time | | `png_set_compression_level()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_IMG_COMPRESSION_LEVEL` | | `png_set_compression_mem_level()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_IMG_MEM_LEVEL` | | `png_set_compression_strategy()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_IMG_COMPRESSION_STRATEGY` | | `png_set_compression_window_bits()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_IMG_WINDOW_BITS` | | `png_set_compression_method()` | N/A | Not supported | | `png_set_text_compression_level()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_TEXT_COMPRESSION_LEVEL` | | `png_set_text_compression_mem_level()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_TEXT_MEM_LEVEL` | | `png_set_text_compression_strategy()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_TEXT_COMPRESSION_STRATEGY` | | `png_set_text_compression_window_bits()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_TEXT_WINDOW_BITS` | | `png_set_text_compression_method()` | N/A | Not supported | | `png_set_filter()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_FILTER_CHOICE` (see [Filter choices](#filter-choices)) | | `png_set_chunk_cache_max()` | [`spng_set_option()`](context.md#spng_set_option) | `SPNG_CHUNK_COUNT_LIMIT` | | `png_set_chunk_malloc_max()` | [`spng_set_chunk_limits()`](#spng_set_chunk_limits) | `chunk_size` argument | | `png_get_chunk_malloc_max()` | [`spng_get_chunk_limits()`](#spng_get_chunk_limits) | `chunk_size` argument | | `png_set_quantize()` | N/A | Not supported | ## Encoding images See also: [Progressive image encoding](encode.md#progressive-image-encoding) !!! note Row pointers (array of pointers) are not used. | libpng | spng | Notes | |--------------------------------|--------------------------------------------------------|---------------------------------------------| | `png_write_row()` | [`spng_encode_row()`](encode.md#spng_encode_row) | | | `png_write_image()` | [`spng_encode_image()`](encode.md#spng_encode_image) | | | `png_write_info_before_PLTE()` | N/A | Not supported | | `png_write_info()` | [`spng_encode_chunks()`](encode.md#spng_encode_chunks) | Optional, chunks are written on-demand | | `png_write_end()` | [`spng_encode_chunks()`](encode.md#spng_encode_chunks) | Can be replaced with `SPNG_ENCODE_FINALIZE` | # Common ## Chunk location constants | libpng | spng | |------------------|-------------------| | `PNG_HAVE_IHDR` | `SPNG_AFTER_IHDR` | | `PNG_HAVE_PLTE` | `SPNG_AFTER_PLTE` | | `PNG_AFTER_IDAT` | `SPNG_AFTER_IDAT` | ## Color type constants | libpng | spng | |-----------------------------|-----------------------------------| | `PNG_COLOR_TYPE_GRAY` | `SPNG_COLOR_TYPE_GRAYSCALE` | | `PNG_COLOR_TYPE_PALETTE` | `SPNG_COLOR_TYPE_INDEXED` | | `PNG_COLOR_TYPE_RGB` | `SPNG_COLOR_TYPE_TRUECOLOR` | | `PNG_COLOR_TYPE_RGB_ALPHA` | `SPNG_COLOR_TYPE_TRUECOLOR_ALPHA` | | `PNG_COLOR_TYPE_GRAY_ALPHA` | `SPNG_COLOR_TYPE_GRAYSCALE_ALPHA` | | `PNG_COLOR_TYPE_RGBA` | `SPNG_COLOR_TYPE_TRUECOLOR_ALPHA` | | `PNG_COLOR_TYPE_GA` | `SPNG_COLOR_TYPE_GRAYSCALE_ALPHA` | ## Filter constants ### Filter values Filter values defined by the standard. | libpng | spng | |--------------------------|-----------------------| | `PNG_FILTER_VALUE_NONE` | `SPNG_FILTER_NONE` | | `PNG_FILTER_VALUE_SUB ` | `SPNG_FILTER_SUB` | | `PNG_FILTER_VALUE_UP` | `SPNG_FILTER_UP` | | `PNG_FILTER_VALUE_AVG` | `SPNG_FILTER_AVERAGE` | | `PNG_FILTER_VALUE_PAETH` | `SPNG_FILTER_PAETH` | ### Filter choices Filter choice flags for [`spng_set_option()`](context.md#spng_set_option) when used with `SPNG_FILTER_CHOICE`. | libpng | spng | |--------------------|----------------------------| | `PNG_NO_FILTERS` | `SPNG_DISABLE_FILTERING` | | `PNG_FILTER_NONE` | `SPNG_FILTER_CHOICE_NONE` | | `PNG_FILTER_SUB` | `SPNG_FILTER_CHOICE_SUB` | | `PNG_FILTER_UP` | `SPNG_FILTER_CHOICE_UP` | | `PNG_FILTER_AVG` | `SPNG_FILTER_CHOICE_AVG` | | `PNG_FILTER_PAETH` | `SPNG_FILTER_CHOICE_PAETH` | | `PNG_ALL_FILTERS` | `SPNG_FILTER_CHOICE_ALL` | ## Miscellaneous functions | libpng | spng | Notes | |-------------------------------------|---------------------------------------------|---------------------------------------------------| | `png_create_info_struct()` | N/A | See [Contexts](#contexts) | | `png_set_mem_fn()` | N/A | Use [`spng_ctx_new2()`](context.md#spng_ctx_new2) | | `png_get_mem_ptr()` | N/A | Not supported | | `png_get_current_row_number()` | [`spng_get_row_info()`](#spng_get_row_info) | `spng_row_info.row_num` | | `png_get_current_pass_number()` | [`spng_get_row_info()`](#spng_get_row_info) | `spng_row_info.pass` | | `png_get_io_state()` | N/A | Not supported | | `png_set_option()` | `spng_set_option()` | | | `png_set_invalid()` | N/A | Not supported | | `png_set_sig_bytes()` | N/A | Not supported | | `png_sig_cmp()` | N/A | Not supported | | `png_check_sig()` | N/A | Not supported | | `png_get_compression_buffer_size()` | N/A | Not supported | | `png_set_compression_buffer_size()` | N/A | Not supported | | `png_jmpbuf()` | N/A | Not supported | | `png_reset_zstream()` | N/A | Not supported | | `png_write_sig()` | N/A | Not supported | | `png_write_chunk()` | N/A | Not supported | | `png_write_chunk_start()` | N/A | Not supported | | `png_write_chunk_data()` | N/A | Not supported | | `png_write_chunk_end()` | N/A | Not supported | libspng-0.7.4/docs/usage.md000066400000000000000000000015641442617636000155550ustar00rootroot00000000000000# Basic usage libspng can decode images to an explicit output format (e.g. 8/16-bit RGBA) from any PNG format. With `SPNG_FMT_PNG` the pixel format will identical to the PNG's format in host-endian, or big-endian with `SPNG_FMT_RAW`, the latter does not support any transforms e.g. gamma correction. ```c #include /* Create a context */ spng_ctx *ctx = spng_ctx_new(0); /* Set an input buffer */ spng_set_png_buffer(ctx, buf, buf_size); /* Calculate output image size */ spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size); /* Get an 8-bit RGBA image regardless of PNG format */ spng_decode_image(ctx, SPNG_FMT_RGBA8, out, out_size, 0); /* Free context memory */ spng_ctx_free(ctx); ``` For a complete example see [example.c](https://github.com/randy408/libspng/blob/v0.7.4/examples/example.c) and [Decoding untrusted files](decode.md#decoding-untrusted-files) libspng-0.7.4/docs/version.md000066400000000000000000000012131442617636000161250ustar00rootroot00000000000000# Versioning Releases follow the [semantic versioning](https://semver.org/) scheme with additional guarantees: * Releases from 0.4.0 to 0.8.x are stable * If 1.0.0 will introduce breaking changes then 0.8.x will be maintained as a separate stable branch Currently 1.0.0 is planned to be [compatible](https://github.com/randy408/libspng/issues/3). # Macros `SPNG_VERSION_MAJOR` libspng version's major number `SPNG_VERSION_MINOR` libspng version's minor number `SPNG_VERSION_PATCH` libspng version's patch number # Functions # spng_version_string() ```c const char *spng_version_string(void) ``` Returns the library version as a string. libspng-0.7.4/examples/000077500000000000000000000000001442617636000150075ustar00rootroot00000000000000libspng-0.7.4/examples/example.c000066400000000000000000000165421442617636000166160ustar00rootroot00000000000000#include #include #include int encode_image(void *image, size_t length, uint32_t width, uint32_t height, enum spng_color_type color_type, int bit_depth) { int fmt; int ret = 0; spng_ctx *ctx = NULL; struct spng_ihdr ihdr = {0}; /* zero-initialize to set valid defaults */ /* Creating an encoder context requires a flag */ ctx = spng_ctx_new(SPNG_CTX_ENCODER); /* Encode to internal buffer managed by the library */ spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1); /* Alternatively you can set an output FILE* or stream with spng_set_png_file() or spng_set_png_stream() */ /* Set image properties, this determines the destination image format */ ihdr.width = width; ihdr.height = height; ihdr.color_type = color_type; ihdr.bit_depth = bit_depth; /* Valid color type, bit depth combinations: https://www.w3.org/TR/2003/REC-PNG-20031110/#table111 */ spng_set_ihdr(ctx, &ihdr); /* When encoding fmt is the source format */ /* SPNG_FMT_PNG is a special value that matches the format in ihdr */ fmt = SPNG_FMT_PNG; /* SPNG_ENCODE_FINALIZE will finalize the PNG with the end-of-file marker */ ret = spng_encode_image(ctx, image, length, fmt, SPNG_ENCODE_FINALIZE); if(ret) { printf("spng_encode_image() error: %s\n", spng_strerror(ret)); goto encode_error; } size_t png_size; void *png_buf = NULL; /* Get the internal buffer of the finished PNG */ png_buf = spng_get_png_buffer(ctx, &png_size, &ret); if(png_buf == NULL) { printf("spng_get_png_buffer() error: %s\n", spng_strerror(ret)); } /* User owns the buffer after a successful call */ free(png_buf); encode_error: spng_ctx_free(ctx); return ret; } const char *color_type_str(enum spng_color_type color_type) { switch(color_type) { case SPNG_COLOR_TYPE_GRAYSCALE: return "grayscale"; case SPNG_COLOR_TYPE_TRUECOLOR: return "truecolor"; case SPNG_COLOR_TYPE_INDEXED: return "indexed color"; case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: return "grayscale with alpha"; case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: return "truecolor with alpha"; default: return "(invalid)"; } } int main(int argc, char **argv) { FILE *png; int ret = 0; spng_ctx *ctx = NULL; unsigned char *image = NULL; if(argc < 2) { printf("no input file\n"); goto error; } png = fopen(argv[1], "rb"); if(png == NULL) { printf("error opening input file %s\n", argv[1]); goto error; } ctx = spng_ctx_new(0); if(ctx == NULL) { printf("spng_ctx_new() failed\n"); goto error; } /* Ignore and don't calculate chunk CRC's */ spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE); /* Set memory usage limits for storing standard and unknown chunks, this is important when reading untrusted files! */ size_t limit = 1024 * 1024 * 64; spng_set_chunk_limits(ctx, limit, limit); /* Set source PNG */ spng_set_png_file(ctx, png); /* or _buffer(), _stream() */ struct spng_ihdr ihdr; ret = spng_get_ihdr(ctx, &ihdr); if(ret) { printf("spng_get_ihdr() error: %s\n", spng_strerror(ret)); goto error; } const char *color_name = color_type_str(ihdr.color_type); printf("width: %u\n" "height: %u\n" "bit depth: %u\n" "color type: %u - %s\n", ihdr.width, ihdr.height, ihdr.bit_depth, ihdr.color_type, color_name); printf("compression method: %u\n" "filter method: %u\n" "interlace method: %u\n", ihdr.compression_method, ihdr.filter_method, ihdr.interlace_method); struct spng_plte plte = {0}; ret = spng_get_plte(ctx, &plte); if(ret && ret != SPNG_ECHUNKAVAIL) { printf("spng_get_plte() error: %s\n", spng_strerror(ret)); goto error; } if(!ret) printf("palette entries: %u\n", plte.n_entries); size_t image_size, image_width; /* Output format, does not depend on source PNG format except for SPNG_FMT_PNG, which is the PNG's format in host-endian or big-endian for SPNG_FMT_RAW. Note that for these two formats <8-bit images are left byte-packed */ int fmt = SPNG_FMT_PNG; /* With SPNG_FMT_PNG indexed color images are output as palette indices, pick another format to expand them. */ if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) fmt = SPNG_FMT_RGB8; ret = spng_decoded_image_size(ctx, fmt, &image_size); if(ret) goto error; image = malloc(image_size); if(image == NULL) goto error; /* Decode the image in one go */ /* ret = spng_decode_image(ctx, image, image_size, SPNG_FMT_RGBA8, 0); if(ret) { printf("spng_decode_image() error: %s\n", spng_strerror(ret)); goto error; }*/ /* Alternatively you can decode the image progressively, this requires an initialization step. */ ret = spng_decode_image(ctx, NULL, 0, fmt, SPNG_DECODE_PROGRESSIVE); if(ret) { printf("progressive spng_decode_image() error: %s\n", spng_strerror(ret)); goto error; } /* ihdr.height will always be non-zero if spng_get_ihdr() succeeds */ image_width = image_size / ihdr.height; struct spng_row_info row_info = {0}; do { ret = spng_get_row_info(ctx, &row_info); if(ret) break; ret = spng_decode_row(ctx, image + row_info.row_num * image_width, image_width); } while(!ret); if(ret != SPNG_EOI) { printf("progressive decode error: %s\n", spng_strerror(ret)); if(ihdr.interlace_method) printf("last pass: %d, scanline: %u\n", row_info.pass, row_info.scanline_idx); else printf("last row: %u\n", row_info.row_num); } uint32_t n_text = 0; struct spng_text *text = NULL; ret = spng_get_text(ctx, NULL, &n_text); if(ret == SPNG_ECHUNKAVAIL) /* No text chunks in file */ { ret = 0; goto no_text; } if(ret) { printf("spng_get_text() error: %s\n", spng_strerror(ret)); goto error; } text = malloc(n_text * sizeof(struct spng_text)); if(text == NULL) goto error; ret = spng_get_text(ctx, text, &n_text); if(ret) { printf("spng_get_text() error: %s\n", spng_strerror(ret)); goto no_text; } uint32_t i; for(i=0; i < n_text; i++) { char *type_str = "tEXt"; if(text[i].type == SPNG_ITXT) type_str = "iTXt"; else if(text[i].type == SPNG_ZTXT) type_str = "zTXt"; printf("\ntext type: %s\n", type_str); printf("keyword: %s\n", text[i].keyword); if(text[i].type == SPNG_ITXT) { printf("language tag: %s\n", text[i].language_tag); printf("translated keyword: %s\n", text[i].translated_keyword); } printf("text is %scompressed\n", text[i].compression_flag ? "" : "not "); printf("text length: %lu\n", (unsigned long int)text[i].length); printf("text: %s\n", text[i].text); } no_text: free(text); /* This example assumes a non-paletted image */ if(fmt == SPNG_FMT_PNG) { ret = encode_image(image, image_size, ihdr.width, ihdr.height, ihdr.color_type, ihdr.bit_depth); } error: spng_ctx_free(ctx); free(image); return ret; } libspng-0.7.4/examples/meson.build000066400000000000000000000002601442617636000171470ustar00rootroot00000000000000if get_option('build_examples') == false and get_option('dev_build') == false subdir_done() endif example_exe = executable('example', 'example.c', dependencies : spng_dep)libspng-0.7.4/meson.build000066400000000000000000000040331442617636000153330ustar00rootroot00000000000000project('spng', 'c', version : '0.7.4', license : [ 'BSD-2-Clause', 'libpng-2.0' ], default_options : 'c_std=c99' ) spng_args = [] static_subproject = false if get_option('default_library') == 'static' spng_args = '-DSPNG_STATIC' static_subproject = meson.is_subproject() endif cc = meson.get_compiler('c') if get_option('enable_opt') == false add_project_arguments('-DSPNG_DISABLE_OPT', language : 'c') elif cc.get_argument_syntax() == 'gcc' and host_machine.system() == 'x86' add_project_arguments('-msse2', language : 'c') endif # Check for GNU target_clones attribute if cc.links(files('tests/target_clones.c'), args : '-Werror', name : 'have target_clones') add_project_arguments('-DSPNG_ENABLE_TARGET_CLONES', language : 'c') endif if get_option('use_miniz') == true add_project_arguments('-DSPNG_USE_MINIZ', language : 'c') zlib_dep = dependency('miniz', fallback : [ 'miniz', 'miniz_dep']) else zlib_dep = dependency('zlib', required : false, fallback : ['zlib', 'zlib_dep'], static : get_option('static_zlib')) if not zlib_dep.found() zlib_dep = cc.find_library('z') endif endif m_dep = cc.find_library('m', required : false) spng_deps = [ zlib_dep, m_dep ] spng_inc = include_directories('spng') spng_src = files('spng/spng.c') spng_lib = library('spng', spng_src, c_args : spng_args, dependencies : spng_deps, install : not static_subproject, version : '0.7.4' ) spng_dep = declare_dependency( link_with : spng_lib, compile_args : spng_args, include_directories : spng_inc, version : meson.project_version() ) if meson.version().version_compare('>= 0.54.0') meson.override_dependency('spng', spng_dep) endif subdir('examples') subdir('tests') if get_option('benchmarks') == true subproject('spngt') endif if static_subproject subdir_done() endif install_headers('spng/spng.h') pkg = import('pkgconfig') pkg.generate(spng_lib, extra_cflags : spng_args, description : 'PNG decoding and encoding library' ) libspng-0.7.4/meson_options.txt000066400000000000000000000016241442617636000166310ustar00rootroot00000000000000option('dev_build', type : 'boolean', value : false, description : 'Enable the testsuite, requires libpng') option('enable_opt', type : 'boolean', value : true, description : 'Enable architecture-specific optimizations') option('use_miniz', type : 'boolean', value : false, description : 'Compile with miniz instead of zlib, disables some features') option('static_zlib', type : 'boolean', value : false, description : 'Link zlib statically') option('benchmarks', type : 'boolean', value : false, description : 'Enable benchmarks, requires Git LFS') option('build_examples', type : 'boolean', value : true, description : 'Build examples, overriden by dev_build') # Not for end-users option('multithreading', type : 'feature', value : 'disabled', description : 'Experimental multithreading features') option('oss_fuzz', type : 'boolean', value : false, description : 'Enable regression tests with OSS-Fuzz corpora')libspng-0.7.4/mkdocs.yml000066400000000000000000000017051442617636000151770ustar00rootroot00000000000000site_name: libspng v0.7.4 site_url: https://libspng.org/docs repo_url: https://github.com/randy408/libspng/ site_description: libspng documentation site_author: Randy copyright: Š 2018-2023, Randy nav: - 'libspng documentation': 'index.md' - 'Build': 'build.md' - 'Usage': 'usage.md' - 'API Documentation': 'api.md' - 'Migration guide - libpng': 'migrate-libpng.md' theme: name: material palette: - media: "(prefers-color-scheme: light)" scheme: default toggle: icon: material/toggle-switch-off-outline name: Switch to dark mode - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/toggle-switch name: Switch to light mode site_dir: 'site' markdown_extensions: - smarty - fenced_code - admonition - codehilite - markdown.extensions.toc: permalink: true plugins: - search libspng-0.7.4/spng/000077500000000000000000000000001442617636000141405ustar00rootroot00000000000000libspng-0.7.4/spng/spng.c000066400000000000000000006057751442617636000152770ustar00rootroot00000000000000/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */ #define SPNG__BUILD #include "spng.h" #include #include #include #include #define ZLIB_CONST #ifdef __FRAMAC__ #define SPNG_DISABLE_OPT #include "tests/framac_stubs.h" #else #ifdef SPNG_USE_MINIZ #include #else #include #endif #endif #ifdef SPNG_MULTITHREADING #include #endif /* Not build options, edit at your own risk! */ #define SPNG_READ_SIZE (8192) #define SPNG_WRITE_SIZE SPNG_READ_SIZE #define SPNG_MAX_CHUNK_COUNT (1000) #define SPNG_TARGET_CLONES(x) #ifndef SPNG_DISABLE_OPT #if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) #define SPNG_X86 #if defined(__x86_64__) || defined(_M_X64) #define SPNG_X86_64 #endif #elif defined(__aarch64__) || defined(_M_ARM64) /* || defined(__ARM_NEON) */ #define SPNG_ARM /* NOTE: only arm64 builds are tested! */ #else #pragma message "disabling SIMD optimizations for unknown target" #define SPNG_DISABLE_OPT #endif #if defined(SPNG_X86_64) && defined(SPNG_ENABLE_TARGET_CLONES) #undef SPNG_TARGET_CLONES #define SPNG_TARGET_CLONES(x) __attribute__((target_clones(x))) #else #define SPNG_TARGET_CLONES(x) #endif #ifndef SPNG_DISABLE_OPT static void defilter_sub3(size_t rowbytes, unsigned char *row); static void defilter_sub4(size_t rowbytes, unsigned char *row); static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev); static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev); static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev); static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev); #if defined(SPNG_ARM) static uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width); static uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width); #endif #endif #endif #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4244) #endif #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || defined(__BIG_ENDIAN__) #define SPNG_BIG_ENDIAN #else #define SPNG_LITTLE_ENDIAN #endif enum spng_state { SPNG_STATE_INVALID = 0, SPNG_STATE_INIT = 1, /* No PNG buffer/stream is set */ SPNG_STATE_INPUT, /* Decoder input PNG was set */ SPNG_STATE_OUTPUT = SPNG_STATE_INPUT, /* Encoder output was set */ SPNG_STATE_IHDR, /* IHDR was read/written */ SPNG_STATE_FIRST_IDAT, /* Encoded up to / reached first IDAT */ SPNG_STATE_DECODE_INIT, /* Decoder is ready for progressive reads */ SPNG_STATE_ENCODE_INIT = SPNG_STATE_DECODE_INIT, SPNG_STATE_EOI, /* Reached the last scanline/row */ SPNG_STATE_LAST_IDAT, /* Reached last IDAT, set at end of decode_image() */ SPNG_STATE_AFTER_IDAT, /* */ SPNG_STATE_IEND, /* Reached IEND */ }; enum spng__internal { SPNG__IO_SIGNAL = 1 << 9, SPNG__CTX_FLAGS_ALL = (SPNG_CTX_IGNORE_ADLER32 | SPNG_CTX_ENCODER) }; #define SPNG_STR(x) _SPNG_STR(x) #define _SPNG_STR(x) #x #define SPNG_VERSION_STRING SPNG_STR(SPNG_VERSION_MAJOR) "." \ SPNG_STR(SPNG_VERSION_MINOR) "." \ SPNG_STR(SPNG_VERSION_PATCH) #define SPNG_GET_CHUNK_BOILERPLATE(chunk) \ if(ctx == NULL) return 1; \ int ret = read_chunks(ctx, 0); \ if(ret) return ret; \ if(!ctx->stored.chunk) return SPNG_ECHUNKAVAIL; \ if(chunk == NULL) return 1 #define SPNG_SET_CHUNK_BOILERPLATE(chunk) \ if(ctx == NULL || chunk == NULL) return 1; \ if(ctx->data == NULL && !ctx->encode_only) return SPNG_ENOSRC; \ int ret = read_chunks(ctx, 0); \ if(ret) return ret /* Determine if the spng_option can be overriden/optimized */ #define spng__optimize(option) (ctx->optimize_option & (1 << option)) struct spng_subimage { uint32_t width; uint32_t height; size_t out_width; /* byte width based on output format */ size_t scanline_width; }; struct spng_text2 { int type; char *keyword; char *text; size_t text_length; uint8_t compression_flag; /* iTXt only */ char *language_tag; /* iTXt only */ char *translated_keyword; /* iTXt only */ size_t cache_usage; char user_keyword_storage[80]; }; struct decode_flags { unsigned apply_trns: 1; unsigned apply_gamma: 1; unsigned use_sbit: 1; unsigned indexed: 1; unsigned do_scaling: 1; unsigned interlaced: 1; unsigned same_layout: 1; unsigned zerocopy: 1; unsigned unpack: 1; }; struct encode_flags { unsigned interlace: 1; unsigned same_layout: 1; unsigned to_bigendian: 1; unsigned progressive: 1; unsigned finalize: 1; enum spng_filter_choice filter_choice; }; struct spng_chunk_bitfield { unsigned ihdr: 1; unsigned plte: 1; unsigned chrm: 1; unsigned iccp: 1; unsigned gama: 1; unsigned sbit: 1; unsigned srgb: 1; unsigned text: 1; unsigned bkgd: 1; unsigned hist: 1; unsigned trns: 1; unsigned phys: 1; unsigned splt: 1; unsigned time: 1; unsigned offs: 1; unsigned exif: 1; unsigned unknown: 1; }; /* Packed sample iterator */ struct spng__iter { const uint8_t mask; unsigned shift_amount; const unsigned initial_shift, bit_depth; const unsigned char *samples; }; union spng__decode_plte { struct spng_plte_entry rgba[256]; unsigned char rgb[256 * 3]; unsigned char raw[256 * 4]; uint32_t align_this; }; struct spng__zlib_options { int compression_level; int window_bits; int mem_level; int strategy; int data_type; }; typedef void spng__undo(spng_ctx *ctx); struct spng_ctx { size_t data_size; size_t bytes_read; size_t stream_buf_size; unsigned char *stream_buf; const unsigned char *data; /* User-defined pointers for streaming */ spng_read_fn *read_fn; spng_write_fn *write_fn; void *stream_user_ptr; /* Used for buffer reads */ const unsigned char *png_base; size_t bytes_left; size_t last_read_size; /* Used for encoding */ int user_owns_out_png; unsigned char *out_png; unsigned char *write_ptr; size_t out_png_size; size_t bytes_encoded; /* These are updated by read/write_header()/read_chunk_bytes() */ struct spng_chunk current_chunk; uint32_t cur_chunk_bytes_left; uint32_t cur_actual_crc; struct spng_alloc alloc; enum spng_ctx_flags flags; enum spng_format fmt; enum spng_state state; unsigned streaming: 1; unsigned internal_buffer: 1; /* encoding to internal buffer */ unsigned inflate: 1; unsigned deflate: 1; unsigned encode_only: 1; unsigned strict: 1; unsigned discard: 1; unsigned skip_crc: 1; unsigned keep_unknown: 1; unsigned prev_was_idat: 1; struct spng__zlib_options image_options; struct spng__zlib_options text_options; spng__undo *undo; /* input file contains this chunk */ struct spng_chunk_bitfield file; /* chunk was stored with spng_set_*() */ struct spng_chunk_bitfield user; /* chunk was stored by reading or with spng_set_*() */ struct spng_chunk_bitfield stored; /* used to reset the above in case of an error */ struct spng_chunk_bitfield prev_stored; struct spng_chunk first_idat, last_idat; uint32_t max_width, max_height; size_t max_chunk_size; size_t chunk_cache_limit; size_t chunk_cache_usage; uint32_t chunk_count_limit; uint32_t chunk_count_total; int crc_action_critical; int crc_action_ancillary; uint32_t optimize_option; struct spng_ihdr ihdr; struct spng_plte plte; struct spng_chrm_int chrm_int; struct spng_iccp iccp; uint32_t gama; struct spng_sbit sbit; uint8_t srgb_rendering_intent; uint32_t n_text; struct spng_text2 *text_list; struct spng_bkgd bkgd; struct spng_hist hist; struct spng_trns trns; struct spng_phys phys; uint32_t n_splt; struct spng_splt *splt_list; struct spng_time time; struct spng_offs offs; struct spng_exif exif; uint32_t n_chunks; struct spng_unknown_chunk *chunk_list; struct spng_subimage subimage[7]; z_stream zstream; unsigned char *scanline_buf, *prev_scanline_buf, *row_buf, *filtered_scanline_buf; unsigned char *scanline, *prev_scanline, *row, *filtered_scanline; /* based on fmt */ size_t image_size; /* may be zero */ size_t image_width; unsigned bytes_per_pixel; /* derived from ihdr */ unsigned pixel_size; /* derived from spng_format+ihdr */ int widest_pass; int last_pass; /* last non-empty pass */ uint16_t *gamma_lut; /* points to either _lut8 or _lut16 */ uint16_t *gamma_lut16; uint16_t gamma_lut8[256]; unsigned char trns_px[8]; union spng__decode_plte decode_plte; struct spng_sbit decode_sb; struct decode_flags decode_flags; struct spng_row_info row_info; struct encode_flags encode_flags; }; static const uint32_t spng_u32max = INT32_MAX; static const uint32_t adam7_x_start[7] = { 0, 4, 0, 2, 0, 1, 0 }; static const uint32_t adam7_y_start[7] = { 0, 0, 4, 0, 2, 0, 1 }; static const uint32_t adam7_x_delta[7] = { 8, 8, 4, 4, 2, 2, 1 }; static const uint32_t adam7_y_delta[7] = { 8, 8, 8, 4, 4, 2, 2 }; static const uint8_t spng_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; static const uint8_t type_ihdr[4] = { 73, 72, 68, 82 }; static const uint8_t type_plte[4] = { 80, 76, 84, 69 }; static const uint8_t type_idat[4] = { 73, 68, 65, 84 }; static const uint8_t type_iend[4] = { 73, 69, 78, 68 }; static const uint8_t type_trns[4] = { 116, 82, 78, 83 }; static const uint8_t type_chrm[4] = { 99, 72, 82, 77 }; static const uint8_t type_gama[4] = { 103, 65, 77, 65 }; static const uint8_t type_iccp[4] = { 105, 67, 67, 80 }; static const uint8_t type_sbit[4] = { 115, 66, 73, 84 }; static const uint8_t type_srgb[4] = { 115, 82, 71, 66 }; static const uint8_t type_text[4] = { 116, 69, 88, 116 }; static const uint8_t type_ztxt[4] = { 122, 84, 88, 116 }; static const uint8_t type_itxt[4] = { 105, 84, 88, 116 }; static const uint8_t type_bkgd[4] = { 98, 75, 71, 68 }; static const uint8_t type_hist[4] = { 104, 73, 83, 84 }; static const uint8_t type_phys[4] = { 112, 72, 89, 115 }; static const uint8_t type_splt[4] = { 115, 80, 76, 84 }; static const uint8_t type_time[4] = { 116, 73, 77, 69 }; static const uint8_t type_offs[4] = { 111, 70, 70, 115 }; static const uint8_t type_exif[4] = { 101, 88, 73, 102 }; static inline void *spng__malloc(spng_ctx *ctx, size_t size) { return ctx->alloc.malloc_fn(size); } static inline void *spng__calloc(spng_ctx *ctx, size_t nmemb, size_t size) { return ctx->alloc.calloc_fn(nmemb, size); } static inline void *spng__realloc(spng_ctx *ctx, void *ptr, size_t size) { return ctx->alloc.realloc_fn(ptr, size); } static inline void spng__free(spng_ctx *ctx, void *ptr) { ctx->alloc.free_fn(ptr); } #if defined(SPNG_USE_MINIZ) static void *spng__zalloc(void *opaque, size_t items, size_t size) #else static void *spng__zalloc(void *opaque, uInt items, uInt size) #endif { spng_ctx *ctx = opaque; if(size > SIZE_MAX / items) return NULL; size_t len = (size_t)items * size; return spng__malloc(ctx, len); } static void spng__zfree(void *opqaue, void *ptr) { spng_ctx *ctx = opqaue; spng__free(ctx, ptr); } static inline uint16_t read_u16(const void *src) { const unsigned char *data = src; return (data[0] & 0xFFU) << 8 | (data[1] & 0xFFU); } static inline uint32_t read_u32(const void *src) { const unsigned char *data = src; return (data[0] & 0xFFUL) << 24 | (data[1] & 0xFFUL) << 16 | (data[2] & 0xFFUL) << 8 | (data[3] & 0xFFUL); } static inline int32_t read_s32(const void *src) { int32_t ret = (int32_t)read_u32(src); return ret; } static inline void write_u16(void *dest, uint16_t x) { unsigned char *data = dest; data[0] = x >> 8; data[1] = x & 0xFF; } static inline void write_u32(void *dest, uint32_t x) { unsigned char *data = dest; data[0] = (x >> 24); data[1] = (x >> 16) & 0xFF; data[2] = (x >> 8) & 0xFF; data[3] = x & 0xFF; } static inline void write_s32(void *dest, int32_t x) { uint32_t n = x; write_u32(dest, n); } /* Returns an iterator for 1,2,4,8-bit samples */ static struct spng__iter spng__iter_init(unsigned bit_depth, const unsigned char *samples) { struct spng__iter iter = { .mask = (uint32_t)(1 << bit_depth) - 1, .shift_amount = 8 - bit_depth, .initial_shift = 8 - bit_depth, .bit_depth = bit_depth, .samples = samples }; return iter; } /* Returns the current sample unpacked, iterates to the next one */ static inline uint8_t get_sample(struct spng__iter *iter) { uint8_t x = (iter->samples[0] >> iter->shift_amount) & iter->mask; iter->shift_amount -= iter->bit_depth; if(iter->shift_amount > 7) { iter->shift_amount = iter->initial_shift; iter->samples++; } return x; } static void u16_row_to_host(void *row, size_t size) { uint16_t *px = row; size_t i, n = size / 2; for(i=0; i < n; i++) { px[i] = read_u16(&px[i]); } } static void u16_row_to_bigendian(void *row, size_t size) { uint16_t *px = (uint16_t*)row; size_t i, n = size / 2; for(i=0; i < n; i++) { write_u16(&px[i], px[i]); } } static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n) { uint32_t i; for(i=0; i < n; i++) { memcpy(out + i * 4, row + i * 3, 3); out[i*4+3] = 255; } } static unsigned num_channels(const struct spng_ihdr *ihdr) { switch(ihdr->color_type) { case SPNG_COLOR_TYPE_TRUECOLOR: return 3; case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: return 2; case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: return 4; case SPNG_COLOR_TYPE_GRAYSCALE: case SPNG_COLOR_TYPE_INDEXED: return 1; default: return 0; } } /* Calculate scanline width in bits, round up to the nearest byte */ static int calculate_scanline_width(const struct spng_ihdr *ihdr, uint32_t width, size_t *scanline_width) { if(ihdr == NULL || !width) return SPNG_EINTERNAL; size_t res = num_channels(ihdr) * ihdr->bit_depth; if(res > SIZE_MAX / width) return SPNG_EOVERFLOW; res = res * width; res += 15; /* Filter byte + 7 for rounding */ if(res < 15) return SPNG_EOVERFLOW; res /= 8; if(res > UINT32_MAX) return SPNG_EOVERFLOW; *scanline_width = res; return 0; } static int calculate_subimages(struct spng_ctx *ctx) { if(ctx == NULL) return SPNG_EINTERNAL; struct spng_ihdr *ihdr = &ctx->ihdr; struct spng_subimage *sub = ctx->subimage; if(ihdr->interlace_method == 1) { sub[0].width = (ihdr->width + 7) >> 3; sub[0].height = (ihdr->height + 7) >> 3; sub[1].width = (ihdr->width + 3) >> 3; sub[1].height = (ihdr->height + 7) >> 3; sub[2].width = (ihdr->width + 3) >> 2; sub[2].height = (ihdr->height + 3) >> 3; sub[3].width = (ihdr->width + 1) >> 2; sub[3].height = (ihdr->height + 3) >> 2; sub[4].width = (ihdr->width + 1) >> 1; sub[4].height = (ihdr->height + 1) >> 2; sub[5].width = ihdr->width >> 1; sub[5].height = (ihdr->height + 1) >> 1; sub[6].width = ihdr->width; sub[6].height = ihdr->height >> 1; } else { sub[0].width = ihdr->width; sub[0].height = ihdr->height; } int i; for(i=0; i < 7; i++) { if(sub[i].width == 0 || sub[i].height == 0) continue; int ret = calculate_scanline_width(ihdr, sub[i].width, &sub[i].scanline_width); if(ret) return ret; if(sub[ctx->widest_pass].scanline_width < sub[i].scanline_width) ctx->widest_pass = i; ctx->last_pass = i; } return 0; } static int check_decode_fmt(const struct spng_ihdr *ihdr, const int fmt) { switch(fmt) { case SPNG_FMT_RGBA8: case SPNG_FMT_RGBA16: case SPNG_FMT_RGB8: case SPNG_FMT_PNG: case SPNG_FMT_RAW: return 0; case SPNG_FMT_G8: case SPNG_FMT_GA8: if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) return 0; else return SPNG_EFMT; case SPNG_FMT_GA16: if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16) return 0; else return SPNG_EFMT; default: return SPNG_EFMT; } } static int calculate_image_width(const struct spng_ihdr *ihdr, int fmt, size_t *len) { if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL; size_t res = ihdr->width; unsigned bytes_per_pixel; switch(fmt) { case SPNG_FMT_RGBA8: case SPNG_FMT_GA16: bytes_per_pixel = 4; break; case SPNG_FMT_RGBA16: bytes_per_pixel = 8; break; case SPNG_FMT_RGB8: bytes_per_pixel = 3; break; case SPNG_FMT_PNG: case SPNG_FMT_RAW: { int ret = calculate_scanline_width(ihdr, ihdr->width, &res); if(ret) return ret; res -= 1; /* exclude filter byte */ bytes_per_pixel = 1; break; } case SPNG_FMT_G8: bytes_per_pixel = 1; break; case SPNG_FMT_GA8: bytes_per_pixel = 2; break; default: return SPNG_EINTERNAL; } if(res > SIZE_MAX / bytes_per_pixel) return SPNG_EOVERFLOW; res = res * bytes_per_pixel; *len = res; return 0; } static int calculate_image_size(const struct spng_ihdr *ihdr, int fmt, size_t *len) { if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL; size_t res = 0; int ret = calculate_image_width(ihdr, fmt, &res); if(ret) return ret; if(res > SIZE_MAX / ihdr->height) return SPNG_EOVERFLOW; res = res * ihdr->height; *len = res; return 0; } static int increase_cache_usage(spng_ctx *ctx, size_t bytes, int new_chunk) { if(ctx == NULL || !bytes) return SPNG_EINTERNAL; if(new_chunk) { ctx->chunk_count_total++; if(ctx->chunk_count_total < 1) return SPNG_EOVERFLOW; if(ctx->chunk_count_total > ctx->chunk_count_limit) return SPNG_ECHUNK_LIMITS; } size_t new_usage = ctx->chunk_cache_usage + bytes; if(new_usage < ctx->chunk_cache_usage) return SPNG_EOVERFLOW; if(new_usage > ctx->chunk_cache_limit) return SPNG_ECHUNK_LIMITS; ctx->chunk_cache_usage = new_usage; return 0; } static int decrease_cache_usage(spng_ctx *ctx, size_t usage) { if(ctx == NULL || !usage) return SPNG_EINTERNAL; if(usage > ctx->chunk_cache_usage) return SPNG_EINTERNAL; ctx->chunk_cache_usage -= usage; return 0; } static int is_critical_chunk(struct spng_chunk *chunk) { if(chunk == NULL) return 0; if((chunk->type[0] & (1 << 5)) == 0) return 1; return 0; } static int decode_err(spng_ctx *ctx, int err) { ctx->state = SPNG_STATE_INVALID; return err; } static int encode_err(spng_ctx *ctx, int err) { ctx->state = SPNG_STATE_INVALID; return err; } static inline int read_data(spng_ctx *ctx, size_t bytes) { if(ctx == NULL) return SPNG_EINTERNAL; if(!bytes) return 0; if(ctx->streaming && (bytes > SPNG_READ_SIZE)) return SPNG_EINTERNAL; int ret = ctx->read_fn(ctx, ctx->stream_user_ptr, ctx->stream_buf, bytes); if(ret) { if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR; return ret; } ctx->bytes_read += bytes; if(ctx->bytes_read < bytes) return SPNG_EOVERFLOW; return 0; } /* Ensure there is enough space for encoding starting at ctx->write_ptr */ static int require_bytes(spng_ctx *ctx, size_t bytes) { if(ctx == NULL) return SPNG_EINTERNAL; if(ctx->streaming) { if(bytes > ctx->stream_buf_size) { size_t new_size = ctx->stream_buf_size; /* Start at default IDAT size + header + crc */ if(new_size < (SPNG_WRITE_SIZE + 12)) new_size = SPNG_WRITE_SIZE + 12; if(new_size < bytes) new_size = bytes; void *temp = spng__realloc(ctx, ctx->stream_buf, new_size); if(temp == NULL) return encode_err(ctx, SPNG_EMEM); ctx->stream_buf = temp; ctx->stream_buf_size = bytes; ctx->write_ptr = ctx->stream_buf; } return 0; } if(!ctx->internal_buffer) return SPNG_ENODST; size_t required = ctx->bytes_encoded + bytes; if(required < bytes) return SPNG_EOVERFLOW; if(required > ctx->out_png_size) { size_t new_size = ctx->out_png_size; /* Start with a size that doesn't require a realloc() 100% of the time */ if(new_size < (SPNG_WRITE_SIZE * 2)) new_size = SPNG_WRITE_SIZE * 2; /* Prefer the next power of two over the requested size */ while(new_size < required) { if(new_size / SIZE_MAX > 2) return encode_err(ctx, SPNG_EOVERFLOW); new_size *= 2; } void *temp = spng__realloc(ctx, ctx->out_png, new_size); if(temp == NULL) return encode_err(ctx, SPNG_EMEM); ctx->out_png = temp; ctx->out_png_size = new_size; ctx->write_ptr = ctx->out_png + ctx->bytes_encoded; } return 0; } static int write_data(spng_ctx *ctx, const void *data, size_t bytes) { if(ctx == NULL) return SPNG_EINTERNAL; if(!bytes) return 0; if(ctx->streaming) { if(bytes > SPNG_WRITE_SIZE) return SPNG_EINTERNAL; int ret = ctx->write_fn(ctx, ctx->stream_user_ptr, (void*)data, bytes); if(ret) { if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR; return encode_err(ctx, ret); } } else { int ret = require_bytes(ctx, bytes); if(ret) return encode_err(ctx, ret); memcpy(ctx->write_ptr, data, bytes); ctx->write_ptr += bytes; } ctx->bytes_encoded += bytes; if(ctx->bytes_encoded < bytes) return SPNG_EOVERFLOW; return 0; } static int write_header(spng_ctx *ctx, const uint8_t chunk_type[4], size_t chunk_length, unsigned char **data) { if(ctx == NULL || chunk_type == NULL) return SPNG_EINTERNAL; if(chunk_length > spng_u32max) return SPNG_EINTERNAL; size_t total = chunk_length + 12; int ret = require_bytes(ctx, total); if(ret) return ret; uint32_t crc = crc32(0, NULL, 0); ctx->current_chunk.crc = crc32(crc, chunk_type, 4); memcpy(&ctx->current_chunk.type, chunk_type, 4); ctx->current_chunk.length = (uint32_t)chunk_length; if(!data) return SPNG_EINTERNAL; if(ctx->streaming) *data = ctx->stream_buf + 8; else *data = ctx->write_ptr + 8; return 0; } static int trim_chunk(spng_ctx *ctx, uint32_t length) { if(length > spng_u32max) return SPNG_EINTERNAL; if(length > ctx->current_chunk.length) return SPNG_EINTERNAL; ctx->current_chunk.length = length; return 0; } static int finish_chunk(spng_ctx *ctx) { if(ctx == NULL) return SPNG_EINTERNAL; struct spng_chunk *chunk = &ctx->current_chunk; unsigned char *header; unsigned char *chunk_data; if(ctx->streaming) { chunk_data = ctx->stream_buf + 8; header = ctx->stream_buf; } else { chunk_data = ctx->write_ptr + 8; header = ctx->write_ptr; } write_u32(header, chunk->length); memcpy(header + 4, chunk->type, 4); chunk->crc = crc32(chunk->crc, chunk_data, chunk->length); write_u32(chunk_data + chunk->length, chunk->crc); if(ctx->streaming) { const unsigned char *ptr = ctx->stream_buf; uint32_t bytes_left = chunk->length + 12; uint32_t len = 0; while(bytes_left) { ptr += len; len = SPNG_WRITE_SIZE; if(len > bytes_left) len = bytes_left; int ret = write_data(ctx, ptr, len); if(ret) return ret; bytes_left -= len; } } else { ctx->bytes_encoded += chunk->length; if(ctx->bytes_encoded < chunk->length) return SPNG_EOVERFLOW; ctx->bytes_encoded += 12; if(ctx->bytes_encoded < 12) return SPNG_EOVERFLOW; ctx->write_ptr += chunk->length + 12; } return 0; } static int write_chunk(spng_ctx *ctx, const uint8_t type[4], const void *data, size_t length) { if(ctx == NULL || type == NULL) return SPNG_EINTERNAL; if(length && data == NULL) return SPNG_EINTERNAL; unsigned char *write_ptr; int ret = write_header(ctx, type, length, &write_ptr); if(ret) return ret; if(length) memcpy(write_ptr, data, length); return finish_chunk(ctx); } static int write_iend(spng_ctx *ctx) { unsigned char iend_chunk[12] = { 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 }; return write_data(ctx, iend_chunk, 12); } static int write_unknown_chunks(spng_ctx *ctx, enum spng_location location) { if(!ctx->stored.unknown) return 0; const struct spng_unknown_chunk *chunk = ctx->chunk_list; uint32_t i; for(i=0; i < ctx->n_chunks; i++, chunk++) { if(chunk->location != location) continue; int ret = write_chunk(ctx, chunk->type, chunk->data, chunk->length); if(ret) return ret; } return 0; } /* Read and check the current chunk's crc, returns -SPNG_CRC_DISCARD if the chunk should be discarded */ static inline int read_and_check_crc(spng_ctx *ctx) { if(ctx == NULL) return SPNG_EINTERNAL; int ret; ret = read_data(ctx, 4); if(ret) return ret; ctx->current_chunk.crc = read_u32(ctx->data); if(ctx->skip_crc) return 0; if(ctx->cur_actual_crc != ctx->current_chunk.crc) { if(is_critical_chunk(&ctx->current_chunk)) { if(ctx->crc_action_critical == SPNG_CRC_USE) return 0; } else { if(ctx->crc_action_ancillary == SPNG_CRC_USE) return 0; if(ctx->crc_action_ancillary == SPNG_CRC_DISCARD) return -SPNG_CRC_DISCARD; } return SPNG_ECHUNK_CRC; } return 0; } /* Read and validate the current chunk's crc and the next chunk header */ static inline int read_header(spng_ctx *ctx) { if(ctx == NULL) return SPNG_EINTERNAL; int ret; struct spng_chunk chunk = { 0 }; ret = read_and_check_crc(ctx); if(ret) { if(ret == -SPNG_CRC_DISCARD) { ctx->discard = 1; } else return ret; } ret = read_data(ctx, 8); if(ret) return ret; chunk.offset = ctx->bytes_read - 8; chunk.length = read_u32(ctx->data); memcpy(&chunk.type, ctx->data + 4, 4); if(chunk.length > spng_u32max) return SPNG_ECHUNK_STDLEN; ctx->cur_chunk_bytes_left = chunk.length; if(is_critical_chunk(&chunk) && ctx->crc_action_critical == SPNG_CRC_USE) ctx->skip_crc = 1; else if(ctx->crc_action_ancillary == SPNG_CRC_USE) ctx->skip_crc = 1; else ctx->skip_crc = 0; if(!ctx->skip_crc) { ctx->cur_actual_crc = crc32(0, NULL, 0); ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, chunk.type, 4); } ctx->current_chunk = chunk; return 0; } /* Read chunk bytes and update crc */ static int read_chunk_bytes(spng_ctx *ctx, uint32_t bytes) { if(ctx == NULL) return SPNG_EINTERNAL; if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL; if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */ int ret; ret = read_data(ctx, bytes); if(ret) return ret; if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, ctx->data, bytes); ctx->cur_chunk_bytes_left -= bytes; return ret; } /* read_chunk_bytes() + read_data() with custom output buffer */ static int read_chunk_bytes2(spng_ctx *ctx, void *out, uint32_t bytes) { if(ctx == NULL) return SPNG_EINTERNAL; if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL; if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */ int ret; uint32_t len = bytes; if(ctx->streaming && len > SPNG_READ_SIZE) len = SPNG_READ_SIZE; while(bytes) { if(len > bytes) len = bytes; ret = ctx->read_fn(ctx, ctx->stream_user_ptr, out, len); if(ret) return ret; if(!ctx->streaming) memcpy(out, ctx->data, len); ctx->bytes_read += len; if(ctx->bytes_read < len) return SPNG_EOVERFLOW; if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, out, len); ctx->cur_chunk_bytes_left -= len; out = (char*)out + len; bytes -= len; len = SPNG_READ_SIZE; } return 0; } static int discard_chunk_bytes(spng_ctx *ctx, uint32_t bytes) { if(ctx == NULL) return SPNG_EINTERNAL; if(!bytes) return 0; int ret; if(ctx->streaming) /* Do small, consecutive reads */ { while(bytes) { uint32_t len = SPNG_READ_SIZE; if(len > bytes) len = bytes; ret = read_chunk_bytes(ctx, len); if(ret) return ret; bytes -= len; } } else { ret = read_chunk_bytes(ctx, bytes); if(ret) return ret; } return 0; } static int spng__inflate_init(spng_ctx *ctx, int window_bits) { if(ctx->zstream.state) inflateEnd(&ctx->zstream); ctx->inflate = 1; ctx->zstream.zalloc = spng__zalloc; ctx->zstream.zfree = spng__zfree; ctx->zstream.opaque = ctx; if(inflateInit2(&ctx->zstream, window_bits) != Z_OK) return SPNG_EZLIB_INIT; #if ZLIB_VERNUM >= 0x1290 && !defined(SPNG_USE_MINIZ) int validate = 1; if(ctx->flags & SPNG_CTX_IGNORE_ADLER32) validate = 0; if(is_critical_chunk(&ctx->current_chunk)) { if(ctx->crc_action_critical == SPNG_CRC_USE) validate = 0; } else /* ancillary */ { if(ctx->crc_action_ancillary == SPNG_CRC_USE) validate = 0; } if(inflateValidate(&ctx->zstream, validate)) return SPNG_EZLIB_INIT; #else /* This requires zlib >= 1.2.11 */ #pragma message ("inflateValidate() not available, SPNG_CTX_IGNORE_ADLER32 will be ignored") #endif return 0; } static int spng__deflate_init(spng_ctx *ctx, struct spng__zlib_options *options) { if(ctx->zstream.state) deflateEnd(&ctx->zstream); ctx->deflate = 1; z_stream *zstream = &ctx->zstream; zstream->zalloc = spng__zalloc; zstream->zfree = spng__zfree; zstream->opaque = ctx; zstream->data_type = options->data_type; int ret = deflateInit2(zstream, options->compression_level, Z_DEFLATED, options->window_bits, options->mem_level, options->strategy); if(ret != Z_OK) return SPNG_EZLIB_INIT; return 0; } /* Inflate a zlib stream starting with start_buf if non-NULL, continuing from the datastream till an end marker, allocating and writing the inflated stream to *out, leaving "extra" bytes at the end, final buffer length is *len. Takes into account the chunk size and cache limits. */ static int spng__inflate_stream(spng_ctx *ctx, char **out, size_t *len, size_t extra, const void *start_buf, size_t start_len) { int ret = spng__inflate_init(ctx, 15); if(ret) return ret; size_t max = ctx->chunk_cache_limit - ctx->chunk_cache_usage; if(ctx->max_chunk_size < max) max = ctx->max_chunk_size; if(extra > max) return SPNG_ECHUNK_LIMITS; max -= extra; uint32_t read_size; size_t size = 8 * 1024; void *t, *buf = spng__malloc(ctx, size); if(buf == NULL) return SPNG_EMEM; z_stream *stream = &ctx->zstream; if(start_buf != NULL && start_len) { stream->avail_in = (uInt)start_len; stream->next_in = start_buf; } else { stream->avail_in = 0; stream->next_in = NULL; } stream->avail_out = (uInt)size; stream->next_out = buf; while(ret != Z_STREAM_END) { ret = inflate(stream, Z_NO_FLUSH); if(ret == Z_STREAM_END) break; if(ret != Z_OK && ret != Z_BUF_ERROR) { ret = SPNG_EZLIB; goto err; } if(!stream->avail_out) /* Resize buffer */ { /* overflow or reached chunk/cache limit */ if( (2 > SIZE_MAX / size) || (size > max / 2) ) { ret = SPNG_ECHUNK_LIMITS; goto err; } size *= 2; t = spng__realloc(ctx, buf, size); if(t == NULL) goto mem; buf = t; stream->avail_out = (uInt)size / 2; stream->next_out = (unsigned char*)buf + size / 2; } else if(!stream->avail_in) /* Read more chunk bytes */ { read_size = ctx->cur_chunk_bytes_left; if(ctx->streaming && read_size > SPNG_READ_SIZE) read_size = SPNG_READ_SIZE; ret = read_chunk_bytes(ctx, read_size); if(ret) { if(!read_size) ret = SPNG_EZLIB; goto err; } stream->avail_in = read_size; stream->next_in = ctx->data; } } size = stream->total_out; if(!size) { ret = SPNG_EZLIB; goto err; } size += extra; if(size < extra) goto mem; t = spng__realloc(ctx, buf, size); if(t == NULL) goto mem; buf = t; (void)increase_cache_usage(ctx, size, 0); *out = buf; *len = size; return 0; mem: ret = SPNG_EMEM; err: spng__free(ctx, buf); return ret; } /* Read at least one byte from the IDAT stream */ static int read_idat_bytes(spng_ctx *ctx, uint32_t *bytes_read) { if(ctx == NULL || bytes_read == NULL) return SPNG_EINTERNAL; if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT; int ret; uint32_t len; while(!ctx->cur_chunk_bytes_left) { ret = read_header(ctx); if(ret) return ret; if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT; } if(ctx->streaming) {/* TODO: estimate bytes to read for progressive reads */ len = SPNG_READ_SIZE; if(len > ctx->cur_chunk_bytes_left) len = ctx->cur_chunk_bytes_left; } else len = ctx->current_chunk.length; ret = read_chunk_bytes(ctx, len); *bytes_read = len; return ret; } static int read_scanline_bytes(spng_ctx *ctx, unsigned char *dest, size_t len) { if(ctx == NULL || dest == NULL) return SPNG_EINTERNAL; int ret = Z_OK; uint32_t bytes_read; z_stream *zstream = &ctx->zstream; zstream->avail_out = (uInt)len; zstream->next_out = dest; while(zstream->avail_out != 0) { ret = inflate(zstream, Z_NO_FLUSH); if(ret == Z_OK) continue; if(ret == Z_STREAM_END) /* Reached an end-marker */ { if(zstream->avail_out != 0) return SPNG_EIDAT_TOO_SHORT; } else if(ret == Z_BUF_ERROR) /* Read more IDAT bytes */ { ret = read_idat_bytes(ctx, &bytes_read); if(ret) return ret; zstream->avail_in = bytes_read; zstream->next_in = ctx->data; } else return SPNG_EIDAT_STREAM; } return 0; } static uint8_t paeth(uint8_t a, uint8_t b, uint8_t c) { int16_t p = a + b - c; int16_t pa = abs(p - a); int16_t pb = abs(p - b); int16_t pc = abs(p - c); if(pa <= pb && pa <= pc) return a; else if(pb <= pc) return b; return c; } SPNG_TARGET_CLONES("default,avx2") static void defilter_up(size_t bytes, unsigned char *row, const unsigned char *prev) { size_t i; for(i=0; i < bytes; i++) { row[i] += prev[i]; } } /* Defilter *scanline in-place. *prev_scanline and *scanline should point to the first pixel, scanline_width is the width of the scanline including the filter byte. */ static int defilter_scanline(const unsigned char *prev_scanline, unsigned char *scanline, size_t scanline_width, unsigned bytes_per_pixel, unsigned filter) { if(prev_scanline == NULL || scanline == NULL || !scanline_width) return SPNG_EINTERNAL; size_t i; scanline_width--; if(filter == 0) return 0; #ifndef SPNG_DISABLE_OPT if(filter == SPNG_FILTER_UP) goto no_opt; if(bytes_per_pixel == 4) { if(filter == SPNG_FILTER_SUB) defilter_sub4(scanline_width, scanline); else if(filter == SPNG_FILTER_AVERAGE) defilter_avg4(scanline_width, scanline, prev_scanline); else if(filter == SPNG_FILTER_PAETH) defilter_paeth4(scanline_width, scanline, prev_scanline); else return SPNG_EFILTER; return 0; } else if(bytes_per_pixel == 3) { if(filter == SPNG_FILTER_SUB) defilter_sub3(scanline_width, scanline); else if(filter == SPNG_FILTER_AVERAGE) defilter_avg3(scanline_width, scanline, prev_scanline); else if(filter == SPNG_FILTER_PAETH) defilter_paeth3(scanline_width, scanline, prev_scanline); else return SPNG_EFILTER; return 0; } no_opt: #endif if(filter == SPNG_FILTER_UP) { defilter_up(scanline_width, scanline, prev_scanline); return 0; } for(i=0; i < scanline_width; i++) { uint8_t x, a, b, c; if(i >= bytes_per_pixel) { a = scanline[i - bytes_per_pixel]; b = prev_scanline[i]; c = prev_scanline[i - bytes_per_pixel]; } else /* First pixel in row */ { a = 0; b = prev_scanline[i]; c = 0; } x = scanline[i]; switch(filter) { case SPNG_FILTER_SUB: { x = x + a; break; } case SPNG_FILTER_AVERAGE: { uint16_t avg = (a + b) / 2; x = x + avg; break; } case SPNG_FILTER_PAETH: { x = x + paeth(a,b,c); break; } } scanline[i] = x; } return 0; } static int filter_scanline(unsigned char *filtered, const unsigned char *prev_scanline, const unsigned char *scanline, size_t scanline_width, unsigned bytes_per_pixel, const unsigned filter) { if(prev_scanline == NULL || scanline == NULL || scanline_width <= 1) return SPNG_EINTERNAL; if(filter > 4) return SPNG_EFILTER; if(filter == 0) return 0; scanline_width--; uint32_t i; for(i=0; i < scanline_width; i++) { uint8_t x, a, b, c; if(i >= bytes_per_pixel) { a = scanline[i - bytes_per_pixel]; b = prev_scanline[i]; c = prev_scanline[i - bytes_per_pixel]; } else /* first pixel in row */ { a = 0; b = prev_scanline[i]; c = 0; } x = scanline[i]; switch(filter) { case SPNG_FILTER_SUB: { x = x - a; break; } case SPNG_FILTER_UP: { x = x - b; break; } case SPNG_FILTER_AVERAGE: { uint16_t avg = (a + b) / 2; x = x - avg; break; } case SPNG_FILTER_PAETH: { x = x - paeth(a,b,c); break; } } filtered[i] = x; } return 0; } static int32_t filter_sum(const unsigned char *prev_scanline, const unsigned char *scanline, size_t size, unsigned bytes_per_pixel, const unsigned filter) { /* prevent potential over/underflow, bails out at a width of ~8M pixels for RGBA8 */ if(size > (INT32_MAX / 128)) return INT32_MAX; uint32_t i; int32_t sum = 0; uint8_t x, a, b, c; for(i=0; i < size; i++) { if(i >= bytes_per_pixel) { a = scanline[i - bytes_per_pixel]; b = prev_scanline[i]; c = prev_scanline[i - bytes_per_pixel]; } else /* first pixel in row */ { a = 0; b = prev_scanline[i]; c = 0; } x = scanline[i]; switch(filter) { case SPNG_FILTER_NONE: { break; } case SPNG_FILTER_SUB: { x = x - a; break; } case SPNG_FILTER_UP: { x = x - b; break; } case SPNG_FILTER_AVERAGE: { uint16_t avg = (a + b) / 2; x = x - avg; break; } case SPNG_FILTER_PAETH: { x = x - paeth(a,b,c); break; } } sum += 128 - abs((int)x - 128); } return sum; } static unsigned get_best_filter(const unsigned char *prev_scanline, const unsigned char *scanline, size_t scanline_width, unsigned bytes_per_pixel, const int choices) { if(!choices) return SPNG_FILTER_NONE; scanline_width--; int i; unsigned int best_filter = 0; enum spng_filter_choice flag; int32_t sum, best_score = INT32_MAX; int32_t filter_scores[5] = { INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX }; if( !(choices & (choices - 1)) ) {/* only one choice/bit is set */ for(i=0; i < 5; i++) { if(choices == 1 << (i + 3)) return i; } } for(i=0; i < 5; i++) { flag = 1 << (i + 3); if(choices & flag) sum = filter_sum(prev_scanline, scanline, scanline_width, bytes_per_pixel, i); else continue; filter_scores[i] = abs(sum); if(filter_scores[i] < best_score) { best_score = filter_scores[i]; best_filter = i; } } return best_filter; } /* Scale "sbits" significant bits in "sample" from "bit_depth" to "target" "bit_depth" must be a valid PNG depth "sbits" must be less than or equal to "bit_depth" "target" must be between 1 and 16 */ static uint16_t sample_to_target(uint16_t sample, unsigned bit_depth, unsigned sbits, unsigned target) { if(bit_depth == sbits) { if(target == sbits) return sample; /* No scaling */ }/* bit_depth > sbits */ else sample = sample >> (bit_depth - sbits); /* Shift significant bits to bottom */ /* Downscale */ if(target < sbits) return sample >> (sbits - target); /* Upscale using left bit replication */ int8_t shift_amount = target - sbits; uint16_t sample_bits = sample; sample = 0; while(shift_amount >= 0) { sample = sample | (sample_bits << shift_amount); shift_amount -= sbits; } int8_t partial = shift_amount + (int8_t)sbits; if(partial != 0) sample = sample | (sample_bits >> abs(shift_amount)); return sample; } static inline void gamma_correct_row(unsigned char *row, uint32_t pixels, int fmt, const uint16_t *gamma_lut) { uint32_t i; if(fmt == SPNG_FMT_RGBA8) { unsigned char *px; for(i=0; i < pixels; i++) { px = row + i * 4; px[0] = gamma_lut[px[0]]; px[1] = gamma_lut[px[1]]; px[2] = gamma_lut[px[2]]; } } else if(fmt == SPNG_FMT_RGBA16) { for(i=0; i < pixels; i++) { uint16_t px[4]; memcpy(px, row + i * 8, 8); px[0] = gamma_lut[px[0]]; px[1] = gamma_lut[px[1]]; px[2] = gamma_lut[px[2]]; memcpy(row + i * 8, px, 8); } } else if(fmt == SPNG_FMT_RGB8) { unsigned char *px; for(i=0; i < pixels; i++) { px = row + i * 3; px[0] = gamma_lut[px[0]]; px[1] = gamma_lut[px[1]]; px[2] = gamma_lut[px[2]]; } } } /* Apply transparency to output row */ static inline void trns_row(unsigned char *row, const unsigned char *scanline, const unsigned char *trns, unsigned scanline_stride, struct spng_ihdr *ihdr, uint32_t pixels, int fmt) { uint32_t i; unsigned row_stride; unsigned depth = ihdr->bit_depth; if(fmt == SPNG_FMT_RGBA8) { if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */ row_stride = 4; for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride) { if(!memcmp(scanline, trns, scanline_stride)) row[3] = 0; } } else if(fmt == SPNG_FMT_RGBA16) { if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */ row_stride = 8; for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride) { if(!memcmp(scanline, trns, scanline_stride)) memset(row + 6, 0, 2); } } else if(fmt == SPNG_FMT_GA8) { row_stride = 2; if(depth == 16) { for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride) { if(!memcmp(scanline, trns, scanline_stride)) memset(row + 1, 0, 1); } } else /* depth <= 8 */ { struct spng__iter iter = spng__iter_init(depth, scanline); for(i=0; i < pixels; i++, row+=row_stride) { if(trns[0] == get_sample(&iter)) row[1] = 0; } } } else if(fmt == SPNG_FMT_GA16) { row_stride = 4; if(depth == 16) { for(i=0; i< pixels; i++, scanline+=scanline_stride, row+=row_stride) { if(!memcmp(scanline, trns, 2)) memset(row + 2, 0, 2); } } else { struct spng__iter iter = spng__iter_init(depth, scanline); for(i=0; i< pixels; i++, row+=row_stride) { if(trns[0] == get_sample(&iter)) memset(row + 2, 0, 2); } } } else return; } static inline void scale_row(unsigned char *row, uint32_t pixels, int fmt, unsigned depth, const struct spng_sbit *sbit) { uint32_t i; if(fmt == SPNG_FMT_RGBA8) { unsigned char px[4]; for(i=0; i < pixels; i++) { memcpy(px, row + i * 4, 4); px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8); px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8); px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8); px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 8); memcpy(row + i * 4, px, 4); } } else if(fmt == SPNG_FMT_RGBA16) { uint16_t px[4]; for(i=0; i < pixels; i++) { memcpy(px, row + i * 8, 8); px[0] = sample_to_target(px[0], depth, sbit->red_bits, 16); px[1] = sample_to_target(px[1], depth, sbit->green_bits, 16); px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 16); px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 16); memcpy(row + i * 8, px, 8); } } else if(fmt == SPNG_FMT_RGB8) { unsigned char px[4]; for(i=0; i < pixels; i++) { memcpy(px, row + i * 3, 3); px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8); px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8); px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8); memcpy(row + i * 3, px, 3); } } else if(fmt == SPNG_FMT_G8) { for(i=0; i < pixels; i++) { row[i] = sample_to_target(row[i], depth, sbit->grayscale_bits, 8); } } else if(fmt == SPNG_FMT_GA8) { for(i=0; i < pixels; i++) { row[i*2] = sample_to_target(row[i*2], depth, sbit->grayscale_bits, 8); } } } /* Expand to *row using 8-bit palette indices from *scanline */ static void expand_row(unsigned char *row, const unsigned char *scanline, const union spng__decode_plte *decode_plte, uint32_t width, int fmt) { uint32_t i = 0; unsigned char *px; unsigned char entry; const struct spng_plte_entry *plte = decode_plte->rgba; #if defined(SPNG_ARM) if(fmt == SPNG_FMT_RGBA8) i = expand_palette_rgba8_neon(row, scanline, decode_plte->raw, width); else if(fmt == SPNG_FMT_RGB8) { i = expand_palette_rgb8_neon(row, scanline, decode_plte->raw, width); for(; i < width; i++) {/* In this case the LUT is 3 bytes packed */ px = row + i * 3; entry = scanline[i]; px[0] = decode_plte->raw[entry * 3 + 0]; px[1] = decode_plte->raw[entry * 3 + 1]; px[2] = decode_plte->raw[entry * 3 + 2]; } return; } #endif if(fmt == SPNG_FMT_RGBA8) { for(; i < width; i++) { px = row + i * 4; entry = scanline[i]; px[0] = plte[entry].red; px[1] = plte[entry].green; px[2] = plte[entry].blue; px[3] = plte[entry].alpha; } } else if(fmt == SPNG_FMT_RGB8) { for(; i < width; i++) { px = row + i * 3; entry = scanline[i]; px[0] = plte[entry].red; px[1] = plte[entry].green; px[2] = plte[entry].blue; } } } /* Unpack 1/2/4/8-bit samples to G8/GA8/GA16 or G16 -> GA16 */ static void unpack_scanline(unsigned char *out, const unsigned char *scanline, uint32_t width, unsigned bit_depth, int fmt) { struct spng__iter iter = spng__iter_init(bit_depth, scanline); uint32_t i; uint16_t sample, alpha = 65535; if(fmt == SPNG_FMT_GA8) goto ga8; else if(fmt == SPNG_FMT_GA16) goto ga16; /* 1/2/4-bit -> 8-bit */ for(i=0; i < width; i++) out[i] = get_sample(&iter); return; ga8: /* 1/2/4/8-bit -> GA8 */ for(i=0; i < width; i++) { out[i*2] = get_sample(&iter); out[i*2 + 1] = 255; } return; ga16: /* 16 -> GA16 */ if(bit_depth == 16) { for(i=0; i < width; i++) { memcpy(out + i * 4, scanline + i * 2, 2); memcpy(out + i * 4 + 2, &alpha, 2); } return; } /* 1/2/4/8-bit -> GA16 */ for(i=0; i < width; i++) { sample = get_sample(&iter); memcpy(out + i * 4, &sample, 2); memcpy(out + i * 4 + 2, &alpha, 2); } } static int check_ihdr(const struct spng_ihdr *ihdr, uint32_t max_width, uint32_t max_height) { if(ihdr->width > spng_u32max || !ihdr->width) return SPNG_EWIDTH; if(ihdr->height > spng_u32max || !ihdr->height) return SPNG_EHEIGHT; if(ihdr->width > max_width) return SPNG_EUSER_WIDTH; if(ihdr->height > max_height) return SPNG_EUSER_HEIGHT; switch(ihdr->color_type) { case SPNG_COLOR_TYPE_GRAYSCALE: { if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 || ihdr->bit_depth == 4 || ihdr->bit_depth == 8 || ihdr->bit_depth == 16) ) return SPNG_EBIT_DEPTH; break; } case SPNG_COLOR_TYPE_TRUECOLOR: case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: { if( !(ihdr->bit_depth == 8 || ihdr->bit_depth == 16) ) return SPNG_EBIT_DEPTH; break; } case SPNG_COLOR_TYPE_INDEXED: { if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 || ihdr->bit_depth == 4 || ihdr->bit_depth == 8) ) return SPNG_EBIT_DEPTH; break; } default: return SPNG_ECOLOR_TYPE; } if(ihdr->compression_method) return SPNG_ECOMPRESSION_METHOD; if(ihdr->filter_method) return SPNG_EFILTER_METHOD; if(ihdr->interlace_method > 1) return SPNG_EINTERLACE_METHOD; return 0; } static int check_plte(const struct spng_plte *plte, const struct spng_ihdr *ihdr) { if(plte == NULL || ihdr == NULL) return 1; if(plte->n_entries == 0) return 1; if(plte->n_entries > 256) return 1; if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) { if(plte->n_entries > (1U << ihdr->bit_depth)) return 1; } return 0; } static int check_sbit(const struct spng_sbit *sbit, const struct spng_ihdr *ihdr) { if(sbit == NULL || ihdr == NULL) return 1; if(ihdr->color_type == 0) { if(sbit->grayscale_bits == 0) return SPNG_ESBIT; if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT; } else if(ihdr->color_type == 2 || ihdr->color_type == 3) { if(sbit->red_bits == 0) return SPNG_ESBIT; if(sbit->green_bits == 0) return SPNG_ESBIT; if(sbit->blue_bits == 0) return SPNG_ESBIT; uint8_t bit_depth; if(ihdr->color_type == 3) bit_depth = 8; else bit_depth = ihdr->bit_depth; if(sbit->red_bits > bit_depth) return SPNG_ESBIT; if(sbit->green_bits > bit_depth) return SPNG_ESBIT; if(sbit->blue_bits > bit_depth) return SPNG_ESBIT; } else if(ihdr->color_type == 4) { if(sbit->grayscale_bits == 0) return SPNG_ESBIT; if(sbit->alpha_bits == 0) return SPNG_ESBIT; if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT; if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT; } else if(ihdr->color_type == 6) { if(sbit->red_bits == 0) return SPNG_ESBIT; if(sbit->green_bits == 0) return SPNG_ESBIT; if(sbit->blue_bits == 0) return SPNG_ESBIT; if(sbit->alpha_bits == 0) return SPNG_ESBIT; if(sbit->red_bits > ihdr->bit_depth) return SPNG_ESBIT; if(sbit->green_bits > ihdr->bit_depth) return SPNG_ESBIT; if(sbit->blue_bits > ihdr->bit_depth) return SPNG_ESBIT; if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT; } return 0; } static int check_chrm_int(const struct spng_chrm_int *chrm_int) { if(chrm_int == NULL) return 1; if(chrm_int->white_point_x > spng_u32max || chrm_int->white_point_y > spng_u32max || chrm_int->red_x > spng_u32max || chrm_int->red_y > spng_u32max || chrm_int->green_x > spng_u32max || chrm_int->green_y > spng_u32max || chrm_int->blue_x > spng_u32max || chrm_int->blue_y > spng_u32max) return SPNG_ECHRM; return 0; } static int check_phys(const struct spng_phys *phys) { if(phys == NULL) return 1; if(phys->unit_specifier > 1) return SPNG_EPHYS; if(phys->ppu_x > spng_u32max) return SPNG_EPHYS; if(phys->ppu_y > spng_u32max) return SPNG_EPHYS; return 0; } static int check_time(const struct spng_time *time) { if(time == NULL) return 1; if(time->month == 0 || time->month > 12) return 1; if(time->day == 0 || time->day > 31) return 1; if(time->hour > 23) return 1; if(time->minute > 59) return 1; if(time->second > 60) return 1; return 0; } static int check_offs(const struct spng_offs *offs) { if(offs == NULL) return 1; if(offs->unit_specifier > 1) return 1; return 0; } static int check_exif(const struct spng_exif *exif) { if(exif == NULL) return 1; if(exif->data == NULL) return 1; if(exif->length < 4) return SPNG_ECHUNK_SIZE; if(exif->length > spng_u32max) return SPNG_ECHUNK_STDLEN; const uint8_t exif_le[4] = { 73, 73, 42, 0 }; const uint8_t exif_be[4] = { 77, 77, 0, 42 }; if(memcmp(exif->data, exif_le, 4) && memcmp(exif->data, exif_be, 4)) return 1; return 0; } /* Validate PNG keyword */ static int check_png_keyword(const char *str) { if(str == NULL) return 1; size_t len = strlen(str); const char *end = str + len; if(!len) return 1; if(len > 79) return 1; if(str[0] == ' ') return 1; /* Leading space */ if(end[-1] == ' ') return 1; /* Trailing space */ if(strstr(str, " ") != NULL) return 1; /* Consecutive spaces */ uint8_t c; while(str != end) { memcpy(&c, str, 1); if( (c >= 32 && c <= 126) || (c >= 161) ) str++; else return 1; /* Invalid character */ } return 0; } /* Validate PNG text *str up to 'len' bytes */ static int check_png_text(const char *str, size_t len) {/* XXX: are consecutive newlines permitted? */ if(str == NULL || len == 0) return 1; uint8_t c; size_t i = 0; while(i < len) { memcpy(&c, str + i, 1); if( (c >= 32 && c <= 126) || (c >= 161) || c == 10) i++; else return 1; /* Invalid character */ } return 0; } /* Returns non-zero for standard chunks which are stored without allocating memory */ static int is_small_chunk(uint8_t type[4]) { if(!memcmp(type, type_plte, 4)) return 1; else if(!memcmp(type, type_chrm, 4)) return 1; else if(!memcmp(type, type_gama, 4)) return 1; else if(!memcmp(type, type_sbit, 4)) return 1; else if(!memcmp(type, type_srgb, 4)) return 1; else if(!memcmp(type, type_bkgd, 4)) return 1; else if(!memcmp(type, type_trns, 4)) return 1; else if(!memcmp(type, type_hist, 4)) return 1; else if(!memcmp(type, type_phys, 4)) return 1; else if(!memcmp(type, type_time, 4)) return 1; else if(!memcmp(type, type_offs, 4)) return 1; else return 0; } static int read_ihdr(spng_ctx *ctx) { int ret; struct spng_chunk *chunk = &ctx->current_chunk; const unsigned char *data; chunk->offset = 8; chunk->length = 13; size_t sizeof_sig_ihdr = 29; ret = read_data(ctx, sizeof_sig_ihdr); if(ret) return ret; data = ctx->data; if(memcmp(data, spng_signature, sizeof(spng_signature))) return SPNG_ESIGNATURE; chunk->length = read_u32(data + 8); memcpy(&chunk->type, data + 12, 4); if(chunk->length != 13) return SPNG_EIHDR_SIZE; if(memcmp(chunk->type, type_ihdr, 4)) return SPNG_ENOIHDR; ctx->cur_actual_crc = crc32(0, NULL, 0); ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, data + 12, 17); ctx->ihdr.width = read_u32(data + 16); ctx->ihdr.height = read_u32(data + 20); ctx->ihdr.bit_depth = data[24]; ctx->ihdr.color_type = data[25]; ctx->ihdr.compression_method = data[26]; ctx->ihdr.filter_method = data[27]; ctx->ihdr.interlace_method = data[28]; ret = check_ihdr(&ctx->ihdr, ctx->max_width, ctx->max_height); if(ret) return ret; ctx->file.ihdr = 1; ctx->stored.ihdr = 1; if(ctx->ihdr.bit_depth < 8) ctx->bytes_per_pixel = 1; else ctx->bytes_per_pixel = num_channels(&ctx->ihdr) * (ctx->ihdr.bit_depth / 8); ret = calculate_subimages(ctx); if(ret) return ret; return 0; } static void splt_undo(spng_ctx *ctx) { struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1]; spng__free(ctx, splt->entries); decrease_cache_usage(ctx, sizeof(struct spng_splt)); decrease_cache_usage(ctx, splt->n_entries * sizeof(struct spng_splt_entry)); splt->entries = NULL; ctx->n_splt--; } static void text_undo(spng_ctx *ctx) { struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1]; spng__free(ctx, text->keyword); if(text->compression_flag) spng__free(ctx, text->text); decrease_cache_usage(ctx, text->cache_usage); decrease_cache_usage(ctx, sizeof(struct spng_text2)); text->keyword = NULL; text->text = NULL; ctx->n_text--; } static void chunk_undo(spng_ctx *ctx) { struct spng_unknown_chunk *chunk = &ctx->chunk_list[ctx->n_chunks - 1]; spng__free(ctx, chunk->data); decrease_cache_usage(ctx, chunk->length); decrease_cache_usage(ctx, sizeof(struct spng_unknown_chunk)); chunk->data = NULL; ctx->n_chunks--; } static int read_non_idat_chunks(spng_ctx *ctx) { int ret; struct spng_chunk chunk; const unsigned char *data; ctx->discard = 0; ctx->undo = NULL; ctx->prev_stored = ctx->stored; while( !(ret = read_header(ctx))) { if(ctx->discard) { if(ctx->undo) ctx->undo(ctx); ctx->stored = ctx->prev_stored; } ctx->discard = 0; ctx->undo = NULL; ctx->prev_stored = ctx->stored; chunk = ctx->current_chunk; if(!memcmp(chunk.type, type_idat, 4)) { if(ctx->state < SPNG_STATE_FIRST_IDAT) { if(ctx->ihdr.color_type == 3 && !ctx->stored.plte) return SPNG_ENOPLTE; ctx->first_idat = chunk; return 0; } if(ctx->prev_was_idat) { /* Ignore extra IDAT's */ ret = discard_chunk_bytes(ctx, chunk.length); if(ret) return ret; continue; } else return SPNG_ECHUNK_POS; /* IDAT chunk not at the end of the IDAT sequence */ } ctx->prev_was_idat = 0; if(is_small_chunk(chunk.type)) { /* None of the known chunks can be zero length */ if(!chunk.length) return SPNG_ECHUNK_SIZE; /* The largest of these chunks is PLTE with 256 entries */ ret = read_chunk_bytes(ctx, chunk.length > 768 ? 768 : chunk.length); if(ret) return ret; } data = ctx->data; if(is_critical_chunk(&chunk)) { if(!memcmp(chunk.type, type_plte, 4)) { if(ctx->file.trns || ctx->file.hist || ctx->file.bkgd) return SPNG_ECHUNK_POS; if(chunk.length % 3 != 0) return SPNG_ECHUNK_SIZE; ctx->plte.n_entries = chunk.length / 3; if(check_plte(&ctx->plte, &ctx->ihdr)) return SPNG_ECHUNK_SIZE; /* XXX: EPLTE? */ size_t i; for(i=0; i < ctx->plte.n_entries; i++) { ctx->plte.entries[i].red = data[i * 3]; ctx->plte.entries[i].green = data[i * 3 + 1]; ctx->plte.entries[i].blue = data[i * 3 + 2]; } ctx->file.plte = 1; ctx->stored.plte = 1; } else if(!memcmp(chunk.type, type_iend, 4)) { if(ctx->state == SPNG_STATE_AFTER_IDAT) { if(chunk.length) return SPNG_ECHUNK_SIZE; ret = read_and_check_crc(ctx); if(ret == -SPNG_CRC_DISCARD) ret = 0; return ret; } else return SPNG_ECHUNK_POS; } else if(!memcmp(chunk.type, type_ihdr, 4)) return SPNG_ECHUNK_POS; else return SPNG_ECHUNK_UNKNOWN_CRITICAL; } else if(!memcmp(chunk.type, type_chrm, 4)) /* Ancillary chunks */ { if(ctx->file.plte) return SPNG_ECHUNK_POS; if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.chrm) return SPNG_EDUP_CHRM; if(chunk.length != 32) return SPNG_ECHUNK_SIZE; ctx->chrm_int.white_point_x = read_u32(data); ctx->chrm_int.white_point_y = read_u32(data + 4); ctx->chrm_int.red_x = read_u32(data + 8); ctx->chrm_int.red_y = read_u32(data + 12); ctx->chrm_int.green_x = read_u32(data + 16); ctx->chrm_int.green_y = read_u32(data + 20); ctx->chrm_int.blue_x = read_u32(data + 24); ctx->chrm_int.blue_y = read_u32(data + 28); if(check_chrm_int(&ctx->chrm_int)) return SPNG_ECHRM; ctx->file.chrm = 1; ctx->stored.chrm = 1; } else if(!memcmp(chunk.type, type_gama, 4)) { if(ctx->file.plte) return SPNG_ECHUNK_POS; if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.gama) return SPNG_EDUP_GAMA; if(chunk.length != 4) return SPNG_ECHUNK_SIZE; ctx->gama = read_u32(data); if(!ctx->gama) return SPNG_EGAMA; if(ctx->gama > spng_u32max) return SPNG_EGAMA; ctx->file.gama = 1; ctx->stored.gama = 1; } else if(!memcmp(chunk.type, type_sbit, 4)) { if(ctx->file.plte) return SPNG_ECHUNK_POS; if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.sbit) return SPNG_EDUP_SBIT; if(ctx->ihdr.color_type == 0) { if(chunk.length != 1) return SPNG_ECHUNK_SIZE; ctx->sbit.grayscale_bits = data[0]; } else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 3) { if(chunk.length != 3) return SPNG_ECHUNK_SIZE; ctx->sbit.red_bits = data[0]; ctx->sbit.green_bits = data[1]; ctx->sbit.blue_bits = data[2]; } else if(ctx->ihdr.color_type == 4) { if(chunk.length != 2) return SPNG_ECHUNK_SIZE; ctx->sbit.grayscale_bits = data[0]; ctx->sbit.alpha_bits = data[1]; } else if(ctx->ihdr.color_type == 6) { if(chunk.length != 4) return SPNG_ECHUNK_SIZE; ctx->sbit.red_bits = data[0]; ctx->sbit.green_bits = data[1]; ctx->sbit.blue_bits = data[2]; ctx->sbit.alpha_bits = data[3]; } if(check_sbit(&ctx->sbit, &ctx->ihdr)) return SPNG_ESBIT; ctx->file.sbit = 1; ctx->stored.sbit = 1; } else if(!memcmp(chunk.type, type_srgb, 4)) { if(ctx->file.plte) return SPNG_ECHUNK_POS; if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.srgb) return SPNG_EDUP_SRGB; if(chunk.length != 1) return SPNG_ECHUNK_SIZE; ctx->srgb_rendering_intent = data[0]; if(ctx->srgb_rendering_intent > 3) return SPNG_ESRGB; ctx->file.srgb = 1; ctx->stored.srgb = 1; } else if(!memcmp(chunk.type, type_bkgd, 4)) { if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.bkgd) return SPNG_EDUP_BKGD; if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4) { if(chunk.length != 2) return SPNG_ECHUNK_SIZE; ctx->bkgd.gray = read_u16(data); } else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6) { if(chunk.length != 6) return SPNG_ECHUNK_SIZE; ctx->bkgd.red = read_u16(data); ctx->bkgd.green = read_u16(data + 2); ctx->bkgd.blue = read_u16(data + 4); } else if(ctx->ihdr.color_type == 3) { if(chunk.length != 1) return SPNG_ECHUNK_SIZE; if(!ctx->file.plte) return SPNG_EBKGD_NO_PLTE; ctx->bkgd.plte_index = data[0]; if(ctx->bkgd.plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX; } ctx->file.bkgd = 1; ctx->stored.bkgd = 1; } else if(!memcmp(chunk.type, type_trns, 4)) { if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.trns) return SPNG_EDUP_TRNS; if(!chunk.length) return SPNG_ECHUNK_SIZE; if(ctx->ihdr.color_type == 0) { if(chunk.length != 2) return SPNG_ECHUNK_SIZE; ctx->trns.gray = read_u16(data); } else if(ctx->ihdr.color_type == 2) { if(chunk.length != 6) return SPNG_ECHUNK_SIZE; ctx->trns.red = read_u16(data); ctx->trns.green = read_u16(data + 2); ctx->trns.blue = read_u16(data + 4); } else if(ctx->ihdr.color_type == 3) { if(chunk.length > ctx->plte.n_entries) return SPNG_ECHUNK_SIZE; if(!ctx->file.plte) return SPNG_ETRNS_NO_PLTE; memcpy(ctx->trns.type3_alpha, data, chunk.length); ctx->trns.n_type3_entries = chunk.length; } if(ctx->ihdr.color_type == 4 || ctx->ihdr.color_type == 6) return SPNG_ETRNS_COLOR_TYPE; ctx->file.trns = 1; ctx->stored.trns = 1; } else if(!memcmp(chunk.type, type_hist, 4)) { if(!ctx->file.plte) return SPNG_EHIST_NO_PLTE; if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.hist) return SPNG_EDUP_HIST; if( (chunk.length / 2) != (ctx->plte.n_entries) ) return SPNG_ECHUNK_SIZE; size_t k; for(k=0; k < (chunk.length / 2); k++) { ctx->hist.frequency[k] = read_u16(data + k*2); } ctx->file.hist = 1; ctx->stored.hist = 1; } else if(!memcmp(chunk.type, type_phys, 4)) { if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.phys) return SPNG_EDUP_PHYS; if(chunk.length != 9) return SPNG_ECHUNK_SIZE; ctx->phys.ppu_x = read_u32(data); ctx->phys.ppu_y = read_u32(data + 4); ctx->phys.unit_specifier = data[8]; if(check_phys(&ctx->phys)) return SPNG_EPHYS; ctx->file.phys = 1; ctx->stored.phys = 1; } else if(!memcmp(chunk.type, type_time, 4)) { if(ctx->file.time) return SPNG_EDUP_TIME; if(chunk.length != 7) return SPNG_ECHUNK_SIZE; struct spng_time time; time.year = read_u16(data); time.month = data[2]; time.day = data[3]; time.hour = data[4]; time.minute = data[5]; time.second = data[6]; if(check_time(&time)) return SPNG_ETIME; ctx->file.time = 1; if(!ctx->user.time) ctx->time = time; ctx->stored.time = 1; } else if(!memcmp(chunk.type, type_offs, 4)) { if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.offs) return SPNG_EDUP_OFFS; if(chunk.length != 9) return SPNG_ECHUNK_SIZE; ctx->offs.x = read_s32(data); ctx->offs.y = read_s32(data + 4); ctx->offs.unit_specifier = data[8]; if(check_offs(&ctx->offs)) return SPNG_EOFFS; ctx->file.offs = 1; ctx->stored.offs = 1; } else /* Arbitrary-length chunk */ { if(!memcmp(chunk.type, type_exif, 4)) { if(ctx->file.exif) return SPNG_EDUP_EXIF; if(!chunk.length) return SPNG_EEXIF; ctx->file.exif = 1; if(ctx->user.exif) goto discard; if(increase_cache_usage(ctx, chunk.length, 1)) return SPNG_ECHUNK_LIMITS; struct spng_exif exif; exif.length = chunk.length; exif.data = spng__malloc(ctx, chunk.length); if(exif.data == NULL) return SPNG_EMEM; ret = read_chunk_bytes2(ctx, exif.data, chunk.length); if(ret) { spng__free(ctx, exif.data); return ret; } if(check_exif(&exif)) { spng__free(ctx, exif.data); return SPNG_EEXIF; } ctx->exif = exif; ctx->stored.exif = 1; } else if(!memcmp(chunk.type, type_iccp, 4)) {/* TODO: add test file with color profile */ if(ctx->file.plte) return SPNG_ECHUNK_POS; if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->file.iccp) return SPNG_EDUP_ICCP; if(!chunk.length) return SPNG_ECHUNK_SIZE; ctx->file.iccp = 1; uint32_t peek_bytes = 81 > chunk.length ? chunk.length : 81; ret = read_chunk_bytes(ctx, peek_bytes); if(ret) return ret; unsigned char *keyword_nul = memchr(ctx->data, '\0', peek_bytes); if(keyword_nul == NULL) return SPNG_EICCP_NAME; uint32_t keyword_len = keyword_nul - ctx->data; if(keyword_len > 79) return SPNG_EICCP_NAME; memcpy(ctx->iccp.profile_name, ctx->data, keyword_len); if(check_png_keyword(ctx->iccp.profile_name)) return SPNG_EICCP_NAME; if(chunk.length < (keyword_len + 2)) return SPNG_ECHUNK_SIZE; if(ctx->data[keyword_len + 1] != 0) return SPNG_EICCP_COMPRESSION_METHOD; ret = spng__inflate_stream(ctx, &ctx->iccp.profile, &ctx->iccp.profile_len, 0, ctx->data + keyword_len + 2, peek_bytes - (keyword_len + 2)); if(ret) return ret; ctx->stored.iccp = 1; } else if(!memcmp(chunk.type, type_text, 4) || !memcmp(chunk.type, type_ztxt, 4) || !memcmp(chunk.type, type_itxt, 4)) { if(!chunk.length) return SPNG_ECHUNK_SIZE; ctx->file.text = 1; if(ctx->user.text) goto discard; if(increase_cache_usage(ctx, sizeof(struct spng_text2), 1)) return SPNG_ECHUNK_LIMITS; ctx->n_text++; if(ctx->n_text < 1) return SPNG_EOVERFLOW; if(sizeof(struct spng_text2) > SIZE_MAX / ctx->n_text) return SPNG_EOVERFLOW; void *buf = spng__realloc(ctx, ctx->text_list, ctx->n_text * sizeof(struct spng_text2)); if(buf == NULL) return SPNG_EMEM; ctx->text_list = buf; struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1]; memset(text, 0, sizeof(struct spng_text2)); ctx->undo = text_undo; uint32_t text_offset = 0, language_tag_offset = 0, translated_keyword_offset = 0; uint32_t peek_bytes = 256; /* enough for 3 80-byte keywords and some text bytes */ uint32_t keyword_len; if(peek_bytes > chunk.length) peek_bytes = chunk.length; ret = read_chunk_bytes(ctx, peek_bytes); if(ret) return ret; data = ctx->data; const unsigned char *zlib_stream = NULL; const unsigned char *peek_end = data + peek_bytes; const unsigned char *keyword_nul = memchr(data, 0, chunk.length > 80 ? 80 : chunk.length); if(keyword_nul == NULL) return SPNG_ETEXT_KEYWORD; keyword_len = keyword_nul - data; if(!memcmp(chunk.type, type_text, 4)) { text->type = SPNG_TEXT; text->text_length = chunk.length - keyword_len - 1; text_offset = keyword_len; /* increment past nul if there is a text field */ if(text->text_length) text_offset++; } else if(!memcmp(chunk.type, type_ztxt, 4)) { text->type = SPNG_ZTXT; if((peek_bytes - keyword_len) <= 2) return SPNG_EZTXT; if(keyword_nul[1]) return SPNG_EZTXT_COMPRESSION_METHOD; text->compression_flag = 1; text_offset = keyword_len + 2; } else if(!memcmp(chunk.type, type_itxt, 4)) { text->type = SPNG_ITXT; /* at least two 1-byte fields, two >=0 length strings, and one byte of (compressed) text */ if((peek_bytes - keyword_len) < 5) return SPNG_EITXT; text->compression_flag = keyword_nul[1]; if(text->compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG; if(keyword_nul[2]) return SPNG_EITXT_COMPRESSION_METHOD; language_tag_offset = keyword_len + 3; const unsigned char *term; term = memchr(data + language_tag_offset, 0, peek_bytes - language_tag_offset); if(term == NULL) return SPNG_EITXT_LANG_TAG; if((peek_end - term) < 2) return SPNG_EITXT; translated_keyword_offset = term - data + 1; zlib_stream = memchr(data + translated_keyword_offset, 0, peek_bytes - translated_keyword_offset); if(zlib_stream == NULL) return SPNG_EITXT; if(zlib_stream == peek_end) return SPNG_EITXT; text_offset = zlib_stream - data + 1; text->text_length = chunk.length - text_offset; } else return SPNG_EINTERNAL; if(text->compression_flag) { /* cache usage = peek_bytes + decompressed text size + nul */ if(increase_cache_usage(ctx, peek_bytes, 0)) return SPNG_ECHUNK_LIMITS; text->keyword = spng__calloc(ctx, 1, peek_bytes); if(text->keyword == NULL) return SPNG_EMEM; memcpy(text->keyword, data, peek_bytes); zlib_stream = ctx->data + text_offset; ret = spng__inflate_stream(ctx, &text->text, &text->text_length, 1, zlib_stream, peek_bytes - text_offset); if(ret) return ret; text->text[text->text_length - 1] = '\0'; text->cache_usage = text->text_length + peek_bytes; } else { if(increase_cache_usage(ctx, chunk.length + 1, 0)) return SPNG_ECHUNK_LIMITS; text->keyword = spng__malloc(ctx, chunk.length + 1); if(text->keyword == NULL) return SPNG_EMEM; memcpy(text->keyword, data, peek_bytes); if(chunk.length > peek_bytes) { ret = read_chunk_bytes2(ctx, text->keyword + peek_bytes, chunk.length - peek_bytes); if(ret) return ret; } text->text = text->keyword + text_offset; text->text_length = chunk.length - text_offset; text->text[text->text_length] = '\0'; text->cache_usage = chunk.length + 1; } if(check_png_keyword(text->keyword)) return SPNG_ETEXT_KEYWORD; text->text_length = strlen(text->text); if(text->type != SPNG_ITXT) { language_tag_offset = keyword_len; translated_keyword_offset = keyword_len; if(ctx->strict && check_png_text(text->text, text->text_length)) { if(text->type == SPNG_ZTXT) return SPNG_EZTXT; else return SPNG_ETEXT; } } text->language_tag = text->keyword + language_tag_offset; text->translated_keyword = text->keyword + translated_keyword_offset; ctx->stored.text = 1; } else if(!memcmp(chunk.type, type_splt, 4)) { if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; if(ctx->user.splt) goto discard; /* XXX: could check profile names for uniqueness */ if(!chunk.length) return SPNG_ECHUNK_SIZE; ctx->file.splt = 1; /* chunk.length + sizeof(struct spng_splt) + splt->n_entries * sizeof(struct spng_splt_entry) */ if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_splt), 1)) return SPNG_ECHUNK_LIMITS; ctx->n_splt++; if(ctx->n_splt < 1) return SPNG_EOVERFLOW; if(sizeof(struct spng_splt) > SIZE_MAX / ctx->n_splt) return SPNG_EOVERFLOW; void *buf = spng__realloc(ctx, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt)); if(buf == NULL) return SPNG_EMEM; ctx->splt_list = buf; struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1]; memset(splt, 0, sizeof(struct spng_splt)); ctx->undo = splt_undo; void *t = spng__malloc(ctx, chunk.length); if(t == NULL) return SPNG_EMEM; splt->entries = t; /* simplifies error handling */ data = t; ret = read_chunk_bytes2(ctx, t, chunk.length); if(ret) return ret; uint32_t keyword_len = chunk.length < 80 ? chunk.length : 80; const unsigned char *keyword_nul = memchr(data, 0, keyword_len); if(keyword_nul == NULL) return SPNG_ESPLT_NAME; keyword_len = keyword_nul - data; memcpy(splt->name, data, keyword_len); if(check_png_keyword(splt->name)) return SPNG_ESPLT_NAME; uint32_t j; for(j=0; j < (ctx->n_splt - 1); j++) { if(!strcmp(ctx->splt_list[j].name, splt->name)) return SPNG_ESPLT_DUP_NAME; } if( (chunk.length - keyword_len) <= 2) return SPNG_ECHUNK_SIZE; splt->sample_depth = data[keyword_len + 1]; uint32_t entries_len = chunk.length - keyword_len - 2; if(!entries_len) return SPNG_ECHUNK_SIZE; if(splt->sample_depth == 16) { if(entries_len % 10 != 0) return SPNG_ECHUNK_SIZE; splt->n_entries = entries_len / 10; } else if(splt->sample_depth == 8) { if(entries_len % 6 != 0) return SPNG_ECHUNK_SIZE; splt->n_entries = entries_len / 6; } else return SPNG_ESPLT_DEPTH; if(!splt->n_entries) return SPNG_ECHUNK_SIZE; size_t list_size = splt->n_entries; if(list_size > SIZE_MAX / sizeof(struct spng_splt_entry)) return SPNG_EOVERFLOW; list_size *= sizeof(struct spng_splt_entry); if(increase_cache_usage(ctx, list_size, 0)) return SPNG_ECHUNK_LIMITS; splt->entries = spng__malloc(ctx, list_size); if(splt->entries == NULL) { spng__free(ctx, t); return SPNG_EMEM; } data = (unsigned char*)t + keyword_len + 2; uint32_t k; if(splt->sample_depth == 16) { for(k=0; k < splt->n_entries; k++) { splt->entries[k].red = read_u16(data + k * 10); splt->entries[k].green = read_u16(data + k * 10 + 2); splt->entries[k].blue = read_u16(data + k * 10 + 4); splt->entries[k].alpha = read_u16(data + k * 10 + 6); splt->entries[k].frequency = read_u16(data + k * 10 + 8); } } else if(splt->sample_depth == 8) { for(k=0; k < splt->n_entries; k++) { splt->entries[k].red = data[k * 6]; splt->entries[k].green = data[k * 6 + 1]; splt->entries[k].blue = data[k * 6 + 2]; splt->entries[k].alpha = data[k * 6 + 3]; splt->entries[k].frequency = read_u16(data + k * 6 + 4); } } spng__free(ctx, t); decrease_cache_usage(ctx, chunk.length); ctx->stored.splt = 1; } else /* Unknown chunk */ { ctx->file.unknown = 1; if(!ctx->keep_unknown) goto discard; if(ctx->user.unknown) goto discard; if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_unknown_chunk), 1)) return SPNG_ECHUNK_LIMITS; ctx->n_chunks++; if(ctx->n_chunks < 1) return SPNG_EOVERFLOW; if(sizeof(struct spng_unknown_chunk) > SIZE_MAX / ctx->n_chunks) return SPNG_EOVERFLOW; void *buf = spng__realloc(ctx, ctx->chunk_list, ctx->n_chunks * sizeof(struct spng_unknown_chunk)); if(buf == NULL) return SPNG_EMEM; ctx->chunk_list = buf; struct spng_unknown_chunk *chunkp = &ctx->chunk_list[ctx->n_chunks - 1]; memset(chunkp, 0, sizeof(struct spng_unknown_chunk)); ctx->undo = chunk_undo; memcpy(chunkp->type, chunk.type, 4); if(ctx->state < SPNG_STATE_FIRST_IDAT) { if(ctx->file.plte) chunkp->location = SPNG_AFTER_PLTE; else chunkp->location = SPNG_AFTER_IHDR; } else if(ctx->state >= SPNG_STATE_AFTER_IDAT) chunkp->location = SPNG_AFTER_IDAT; if(chunk.length > 0) { void *t = spng__malloc(ctx, chunk.length); if(t == NULL) return SPNG_EMEM; ret = read_chunk_bytes2(ctx, t, chunk.length); if(ret) { spng__free(ctx, t); return ret; } chunkp->length = chunk.length; chunkp->data = t; } ctx->stored.unknown = 1; } discard: ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left); if(ret) return ret; } } return ret; } /* Read chunks before or after the IDAT chunks depending on state */ static int read_chunks(spng_ctx *ctx, int only_ihdr) { if(ctx == NULL) return SPNG_EINTERNAL; if(!ctx->state) return SPNG_EBADSTATE; if(ctx->data == NULL) { if(ctx->encode_only) return 0; else return SPNG_EINTERNAL; } int ret = 0; if(ctx->state == SPNG_STATE_INPUT) { ret = read_ihdr(ctx); if(ret) return decode_err(ctx, ret); ctx->state = SPNG_STATE_IHDR; } if(only_ihdr) return 0; if(ctx->state == SPNG_STATE_EOI) { ctx->state = SPNG_STATE_AFTER_IDAT; ctx->prev_was_idat = 1; } while(ctx->state < SPNG_STATE_FIRST_IDAT || ctx->state == SPNG_STATE_AFTER_IDAT) { ret = read_non_idat_chunks(ctx); if(!ret) { if(ctx->state < SPNG_STATE_FIRST_IDAT) ctx->state = SPNG_STATE_FIRST_IDAT; else if(ctx->state == SPNG_STATE_AFTER_IDAT) ctx->state = SPNG_STATE_IEND; } else { switch(ret) { case SPNG_ECHUNK_POS: case SPNG_ECHUNK_SIZE: /* size != expected size, SPNG_ECHUNK_STDLEN = invalid size */ case SPNG_EDUP_PLTE: case SPNG_EDUP_CHRM: case SPNG_EDUP_GAMA: case SPNG_EDUP_ICCP: case SPNG_EDUP_SBIT: case SPNG_EDUP_SRGB: case SPNG_EDUP_BKGD: case SPNG_EDUP_HIST: case SPNG_EDUP_TRNS: case SPNG_EDUP_PHYS: case SPNG_EDUP_TIME: case SPNG_EDUP_OFFS: case SPNG_EDUP_EXIF: case SPNG_ECHRM: case SPNG_ETRNS_COLOR_TYPE: case SPNG_ETRNS_NO_PLTE: case SPNG_EGAMA: case SPNG_EICCP_NAME: case SPNG_EICCP_COMPRESSION_METHOD: case SPNG_ESBIT: case SPNG_ESRGB: case SPNG_ETEXT: case SPNG_ETEXT_KEYWORD: case SPNG_EZTXT: case SPNG_EZTXT_COMPRESSION_METHOD: case SPNG_EITXT: case SPNG_EITXT_COMPRESSION_FLAG: case SPNG_EITXT_COMPRESSION_METHOD: case SPNG_EITXT_LANG_TAG: case SPNG_EITXT_TRANSLATED_KEY: case SPNG_EBKGD_NO_PLTE: case SPNG_EBKGD_PLTE_IDX: case SPNG_EHIST_NO_PLTE: case SPNG_EPHYS: case SPNG_ESPLT_NAME: case SPNG_ESPLT_DUP_NAME: case SPNG_ESPLT_DEPTH: case SPNG_ETIME: case SPNG_EOFFS: case SPNG_EEXIF: case SPNG_EZLIB: { if(!ctx->strict && !is_critical_chunk(&ctx->current_chunk)) { ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left); if(ret) return decode_err(ctx, ret); if(ctx->undo) ctx->undo(ctx); ctx->stored = ctx->prev_stored; ctx->discard = 0; ctx->undo = NULL; continue; } else return decode_err(ctx, ret); break; } default: return decode_err(ctx, ret); } } } return ret; } static int read_scanline(spng_ctx *ctx) { int ret, pass = ctx->row_info.pass; struct spng_row_info *ri = &ctx->row_info; const struct spng_subimage *sub = ctx->subimage; size_t scanline_width = sub[pass].scanline_width; uint32_t scanline_idx = ri->scanline_idx; uint8_t next_filter = 0; if(scanline_idx == (sub[pass].height - 1) && ri->pass == ctx->last_pass) { ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width - 1); } else { ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width); if(ret) return ret; next_filter = ctx->scanline[scanline_width - 1]; if(next_filter > 4) ret = SPNG_EFILTER; } if(ret) return ret; if(!scanline_idx && ri->filter > 1) { /* prev_scanline is all zeros for the first scanline */ memset(ctx->prev_scanline, 0, scanline_width); } if(ctx->ihdr.bit_depth == 16 && ctx->fmt != SPNG_FMT_RAW) u16_row_to_host(ctx->scanline, scanline_width - 1); ret = defilter_scanline(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, ri->filter); if(ret) return ret; ri->filter = next_filter; return 0; } static int update_row_info(spng_ctx *ctx) { int interlacing = ctx->ihdr.interlace_method; struct spng_row_info *ri = &ctx->row_info; const struct spng_subimage *sub = ctx->subimage; if(ri->scanline_idx == (sub[ri->pass].height - 1)) /* Last scanline */ { if(ri->pass == ctx->last_pass) { ctx->state = SPNG_STATE_EOI; return SPNG_EOI; } ri->scanline_idx = 0; ri->pass++; /* Skip empty passes */ while( (!sub[ri->pass].width || !sub[ri->pass].height) && (ri->pass < ctx->last_pass)) ri->pass++; } else { ri->row_num++; ri->scanline_idx++; } if(interlacing) ri->row_num = adam7_y_start[ri->pass] + ri->scanline_idx * adam7_y_delta[ri->pass]; return 0; } int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len) { if(ctx == NULL || out == NULL) return 1; if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; struct decode_flags f = ctx->decode_flags; struct spng_row_info *ri = &ctx->row_info; const struct spng_subimage *sub = ctx->subimage; const struct spng_ihdr *ihdr = &ctx->ihdr; const uint16_t *gamma_lut = ctx->gamma_lut; unsigned char *trns_px = ctx->trns_px; const struct spng_sbit *sb = &ctx->decode_sb; const struct spng_plte_entry *plte = ctx->decode_plte.rgba; struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->scanline); const unsigned char *scanline; const int pass = ri->pass; const int fmt = ctx->fmt; const size_t scanline_width = sub[pass].scanline_width; const uint32_t width = sub[pass].width; uint32_t k; uint8_t r_8, g_8, b_8, a_8, gray_8; uint16_t r_16, g_16, b_16, a_16, gray_16; r_8=0; g_8=0; b_8=0; a_8=0; gray_8=0; r_16=0; g_16=0; b_16=0; a_16=0; gray_16=0; size_t pixel_size = 4; /* SPNG_FMT_RGBA8 */ size_t pixel_offset = 0; unsigned char *pixel; unsigned processing_depth = ihdr->bit_depth; if(f.indexed) processing_depth = 8; if(fmt == SPNG_FMT_RGBA16) pixel_size = 8; else if(fmt == SPNG_FMT_RGB8) pixel_size = 3; if(len < sub[pass].out_width) return SPNG_EBUFSIZ; int ret = read_scanline(ctx); if(ret) return decode_err(ctx, ret); scanline = ctx->scanline; for(k=0; k < width; k++) { pixel = (unsigned char*)out + pixel_offset; pixel_offset += pixel_size; if(f.same_layout) { if(f.zerocopy) break; memcpy(out, scanline, scanline_width - 1); break; } if(f.unpack) { unpack_scanline(out, scanline, width, ihdr->bit_depth, fmt); break; } if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR) { if(ihdr->bit_depth == 16) { memcpy(&r_16, scanline + (k * 6), 2); memcpy(&g_16, scanline + (k * 6) + 2, 2); memcpy(&b_16, scanline + (k * 6) + 4, 2); a_16 = 65535; } else /* == 8 */ { if(fmt == SPNG_FMT_RGBA8) { rgb8_row_to_rgba8(scanline, out, width); break; } r_8 = scanline[k * 3]; g_8 = scanline[k * 3 + 1]; b_8 = scanline[k * 3 + 2]; a_8 = 255; } } else if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) { uint8_t entry = 0; if(ihdr->bit_depth == 8) { if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) { expand_row(out, scanline, &ctx->decode_plte, width, fmt); break; } entry = scanline[k]; } else /* < 8 */ { entry = get_sample(&iter); } if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) { pixel[0] = plte[entry].red; pixel[1] = plte[entry].green; pixel[2] = plte[entry].blue; if(fmt == SPNG_FMT_RGBA8) pixel[3] = plte[entry].alpha; continue; } else /* RGBA16 */ { r_16 = plte[entry].red; g_16 = plte[entry].green; b_16 = plte[entry].blue; a_16 = plte[entry].alpha; r_16 = (r_16 << 8) | r_16; g_16 = (g_16 << 8) | g_16; b_16 = (b_16 << 8) | b_16; a_16 = (a_16 << 8) | a_16; memcpy(pixel, &r_16, 2); memcpy(pixel + 2, &g_16, 2); memcpy(pixel + 4, &b_16, 2); memcpy(pixel + 6, &a_16, 2); continue; } } else if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) { if(ihdr->bit_depth == 16) { memcpy(&r_16, scanline + (k * 8), 2); memcpy(&g_16, scanline + (k * 8) + 2, 2); memcpy(&b_16, scanline + (k * 8) + 4, 2); memcpy(&a_16, scanline + (k * 8) + 6, 2); } else /* == 8 */ { r_8 = scanline[k * 4]; g_8 = scanline[k * 4 + 1]; b_8 = scanline[k * 4 + 2]; a_8 = scanline[k * 4 + 3]; } } else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) { if(ihdr->bit_depth == 16) { memcpy(&gray_16, scanline + k * 2, 2); if(f.apply_trns && ctx->trns.gray == gray_16) a_16 = 0; else a_16 = 65535; r_16 = gray_16; g_16 = gray_16; b_16 = gray_16; } else /* <= 8 */ { gray_8 = get_sample(&iter); if(f.apply_trns && ctx->trns.gray == gray_8) a_8 = 0; else a_8 = 255; r_8 = gray_8; g_8 = gray_8; b_8 = gray_8; } } else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) { if(ihdr->bit_depth == 16) { memcpy(&gray_16, scanline + (k * 4), 2); memcpy(&a_16, scanline + (k * 4) + 2, 2); r_16 = gray_16; g_16 = gray_16; b_16 = gray_16; } else /* == 8 */ { gray_8 = scanline[k * 2]; a_8 = scanline[k * 2 + 1]; r_8 = gray_8; g_8 = gray_8; b_8 = gray_8; } } if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) { if(ihdr->bit_depth == 16) { r_8 = r_16 >> 8; g_8 = g_16 >> 8; b_8 = b_16 >> 8; a_8 = a_16 >> 8; } pixel[0] = r_8; pixel[1] = g_8; pixel[2] = b_8; if(fmt == SPNG_FMT_RGBA8) pixel[3] = a_8; } else if(fmt == SPNG_FMT_RGBA16) { if(ihdr->bit_depth != 16) { r_16 = r_8; g_16 = g_8; b_16 = b_8; a_16 = a_8; } memcpy(pixel, &r_16, 2); memcpy(pixel + 2, &g_16, 2); memcpy(pixel + 4, &b_16, 2); memcpy(pixel + 6, &a_16, 2); } }/* for(k=0; k < width; k++) */ if(f.apply_trns) trns_row(out, scanline, trns_px, ctx->bytes_per_pixel, &ctx->ihdr, width, fmt); if(f.do_scaling) scale_row(out, width, fmt, processing_depth, sb); if(f.apply_gamma) gamma_correct_row(out, width, fmt, gamma_lut); /* The previous scanline is always defiltered */ void *t = ctx->prev_scanline; ctx->prev_scanline = ctx->scanline; ctx->scanline = t; ret = update_row_info(ctx); if(ret == SPNG_EOI) { if(ctx->cur_chunk_bytes_left) /* zlib stream ended before an IDAT chunk boundary */ {/* Discard the rest of the chunk */ int error = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left); if(error) return decode_err(ctx, error); } ctx->last_idat = ctx->current_chunk; } return ret; } int spng_decode_row(spng_ctx *ctx, void *out, size_t len) { if(ctx == NULL || out == NULL) return 1; if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; if(len < ctx->image_width) return SPNG_EBUFSIZ; const struct spng_ihdr *ihdr = &ctx->ihdr; int ret, pass = ctx->row_info.pass; unsigned char *outptr = out; if(!ihdr->interlace_method || pass == 6) return spng_decode_scanline(ctx, out, len); ret = spng_decode_scanline(ctx, ctx->row, ctx->image_width); if(ret && ret != SPNG_EOI) return ret; uint32_t k; unsigned pixel_size = 4; /* RGBA8 */ if(ctx->fmt == SPNG_FMT_RGBA16) pixel_size = 8; else if(ctx->fmt == SPNG_FMT_RGB8) pixel_size = 3; else if(ctx->fmt == SPNG_FMT_G8) pixel_size = 1; else if(ctx->fmt == SPNG_FMT_GA8) pixel_size = 2; else if(ctx->fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) { if(ihdr->bit_depth < 8) { struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->row); const uint8_t samples_per_byte = 8 / ihdr->bit_depth; uint8_t sample; for(k=0; k < ctx->subimage[pass].width; k++) { sample = get_sample(&iter); size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass]; sample = sample << (iter.initial_shift - ioffset * ihdr->bit_depth % 8); ioffset /= samples_per_byte; outptr[ioffset] |= sample; } return 0; } else pixel_size = ctx->bytes_per_pixel; } for(k=0; k < ctx->subimage[pass].width; k++) { size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size; memcpy(outptr + ioffset, ctx->row + k * pixel_size, pixel_size); } return 0; } int spng_decode_chunks(spng_ctx *ctx) { if(ctx == NULL) return 1; if(ctx->encode_only) return SPNG_ECTXTYPE; if(ctx->state < SPNG_STATE_INPUT) return SPNG_ENOSRC; if(ctx->state == SPNG_STATE_IEND) return 0; return read_chunks(ctx, 0); } int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags) { if(ctx == NULL) return 1; if(ctx->encode_only) return SPNG_ECTXTYPE; if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; const struct spng_ihdr *ihdr = &ctx->ihdr; int ret = read_chunks(ctx, 0); if(ret) return decode_err(ctx, ret); ret = check_decode_fmt(ihdr, fmt); if(ret) return ret; ret = calculate_image_width(ihdr, fmt, &ctx->image_width); if(ret) return decode_err(ctx, ret); if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */ else ctx->image_size = ctx->image_width * ihdr->height; if( !(flags & SPNG_DECODE_PROGRESSIVE) ) { if(out == NULL) return 1; if(!ctx->image_size) return SPNG_EOVERFLOW; if(len < ctx->image_size) return SPNG_EBUFSIZ; } uint32_t bytes_read = 0; ret = read_idat_bytes(ctx, &bytes_read); if(ret) return decode_err(ctx, ret); if(bytes_read > 1) { int valid = read_u16(ctx->data) % 31 ? 0 : 1; unsigned flg = ctx->data[1]; unsigned flevel = flg >> 6; int compression_level = Z_DEFAULT_COMPRESSION; if(flevel == 0) compression_level = 0; /* fastest */ else if(flevel == 1) compression_level = 1; /* fast */ else if(flevel == 2) compression_level = 6; /* default */ else if(flevel == 3) compression_level = 9; /* slowest, max compression */ if(valid) ctx->image_options.compression_level = compression_level; } ret = spng__inflate_init(ctx, ctx->image_options.window_bits); if(ret) return decode_err(ctx, ret); ctx->zstream.avail_in = bytes_read; ctx->zstream.next_in = ctx->data; size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width; scanline_buf_size += 32; if(scanline_buf_size < 32) return SPNG_EOVERFLOW; ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size); ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size); ctx->scanline = ctx->scanline_buf; ctx->prev_scanline = ctx->prev_scanline_buf; struct decode_flags f = {0}; ctx->fmt = fmt; if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) f.indexed = 1; unsigned processing_depth = ihdr->bit_depth; if(f.indexed) processing_depth = 8; if(ihdr->interlace_method) { f.interlaced = 1; ctx->row_buf = spng__malloc(ctx, ctx->image_width); ctx->row = ctx->row_buf; if(ctx->row == NULL) return decode_err(ctx, SPNG_EMEM); } if(ctx->scanline == NULL || ctx->prev_scanline == NULL) { return decode_err(ctx, SPNG_EMEM); } f.do_scaling = 1; if(f.indexed) f.do_scaling = 0; unsigned depth_target = 8; /* FMT_RGBA8, G8 */ if(fmt == SPNG_FMT_RGBA16) depth_target = 16; if(flags & SPNG_DECODE_TRNS && ctx->stored.trns) f.apply_trns = 1; else flags &= ~SPNG_DECODE_TRNS; if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) flags &= ~SPNG_DECODE_TRNS; if(flags & SPNG_DECODE_GAMMA && ctx->stored.gama) f.apply_gamma = 1; else flags &= ~SPNG_DECODE_GAMMA; if(flags & SPNG_DECODE_USE_SBIT && ctx->stored.sbit) f.use_sbit = 1; else flags &= ~SPNG_DECODE_USE_SBIT; if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16)) { if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA && ihdr->bit_depth == depth_target) f.same_layout = 1; } else if(fmt == SPNG_FMT_RGB8) { if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR && ihdr->bit_depth == depth_target) f.same_layout = 1; f.apply_trns = 0; /* not applicable */ } else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) { f.same_layout = 1; f.do_scaling = 0; f.apply_gamma = 0; /* for now */ f.apply_trns = 0; } else if(fmt == SPNG_FMT_G8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) { if(ihdr->bit_depth == depth_target) f.same_layout = 1; else if(ihdr->bit_depth < 8) f.unpack = 1; f.apply_trns = 0; } else if(fmt == SPNG_FMT_GA8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) { if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA && ihdr->bit_depth == depth_target) f.same_layout = 1; else if(ihdr->bit_depth <= 8) f.unpack = 1; } else if(fmt == SPNG_FMT_GA16 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16) { if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA && ihdr->bit_depth == depth_target) f.same_layout = 1; else if(ihdr->bit_depth == 16) f.unpack = 1; } /*if(f.same_layout && !flags && !f.interlaced) f.zerocopy = 1;*/ uint16_t *gamma_lut = NULL; if(f.apply_gamma) { float file_gamma = (float)ctx->gama / 100000.0f; float max; unsigned lut_entries; if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) { lut_entries = 256; max = 255.0f; gamma_lut = ctx->gamma_lut8; ctx->gamma_lut = ctx->gamma_lut8; } else /* SPNG_FMT_RGBA16 */ { lut_entries = 65536; max = 65535.0f; ctx->gamma_lut16 = spng__malloc(ctx, lut_entries * sizeof(uint16_t)); if(ctx->gamma_lut16 == NULL) return decode_err(ctx, SPNG_EMEM); gamma_lut = ctx->gamma_lut16; ctx->gamma_lut = ctx->gamma_lut16; } float screen_gamma = 2.2f; float exponent = file_gamma * screen_gamma; if(FP_ZERO == fpclassify(exponent)) return decode_err(ctx, SPNG_EGAMA); exponent = 1.0f / exponent; unsigned i; for(i=0; i < lut_entries; i++) { float c = pow((float)i / max, exponent) * max; if(c > max) c = max; gamma_lut[i] = (uint16_t)c; } } struct spng_sbit *sb = &ctx->decode_sb; sb->red_bits = processing_depth; sb->green_bits = processing_depth; sb->blue_bits = processing_depth; sb->alpha_bits = processing_depth; sb->grayscale_bits = processing_depth; if(f.use_sbit) { if(ihdr->color_type == 0) { sb->grayscale_bits = ctx->sbit.grayscale_bits; sb->alpha_bits = ihdr->bit_depth; } else if(ihdr->color_type == 2 || ihdr->color_type == 3) { sb->red_bits = ctx->sbit.red_bits; sb->green_bits = ctx->sbit.green_bits; sb->blue_bits = ctx->sbit.blue_bits; sb->alpha_bits = ihdr->bit_depth; } else if(ihdr->color_type == 4) { sb->grayscale_bits = ctx->sbit.grayscale_bits; sb->alpha_bits = ctx->sbit.alpha_bits; } else /* == 6 */ { sb->red_bits = ctx->sbit.red_bits; sb->green_bits = ctx->sbit.green_bits; sb->blue_bits = ctx->sbit.blue_bits; sb->alpha_bits = ctx->sbit.alpha_bits; } } if(ihdr->bit_depth == 16 && fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) {/* samples are scaled down by 8 bits in the decode loop */ sb->red_bits -= 8; sb->green_bits -= 8; sb->blue_bits -= 8; sb->alpha_bits -= 8; sb->grayscale_bits -= 8; processing_depth = 8; } /* Prevent infinite loops in sample_to_target() */ if(!depth_target || depth_target > 16 || !processing_depth || processing_depth > 16 || !sb->grayscale_bits || sb->grayscale_bits > processing_depth || !sb->alpha_bits || sb->alpha_bits > processing_depth || !sb->red_bits || sb->red_bits > processing_depth || !sb->green_bits || sb->green_bits > processing_depth || !sb->blue_bits || sb->blue_bits > processing_depth) { return decode_err(ctx, SPNG_ESBIT); } if(sb->red_bits == sb->green_bits && sb->green_bits == sb->blue_bits && sb->blue_bits == sb->alpha_bits && sb->alpha_bits == processing_depth && processing_depth == depth_target) f.do_scaling = 0; struct spng_plte_entry *plte = ctx->decode_plte.rgba; /* Pre-process palette entries */ if(f.indexed) { uint8_t red, green, blue, alpha; uint32_t i; for(i=0; i < 256; i++) { if(f.apply_trns && i < ctx->trns.n_type3_entries) ctx->plte.entries[i].alpha = ctx->trns.type3_alpha[i]; else ctx->plte.entries[i].alpha = 255; red = sample_to_target(ctx->plte.entries[i].red, 8, sb->red_bits, 8); green = sample_to_target(ctx->plte.entries[i].green, 8, sb->green_bits, 8); blue = sample_to_target(ctx->plte.entries[i].blue, 8, sb->blue_bits, 8); alpha = sample_to_target(ctx->plte.entries[i].alpha, 8, sb->alpha_bits, 8); #if defined(SPNG_ARM) if(fmt == SPNG_FMT_RGB8 && ihdr->bit_depth == 8) {/* Working with 3 bytes at a time is more of an ARM thing */ ctx->decode_plte.rgb[i * 3 + 0] = red; ctx->decode_plte.rgb[i * 3 + 1] = green; ctx->decode_plte.rgb[i * 3 + 2] = blue; continue; } #endif plte[i].red = red; plte[i].green = green; plte[i].blue = blue; plte[i].alpha = alpha; } f.apply_trns = 0; } unsigned char *trns_px = ctx->trns_px; if(f.apply_trns) { uint16_t mask = ~0; if(ctx->ihdr.bit_depth < 16) mask = (1 << ctx->ihdr.bit_depth) - 1; if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16)) { if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR) { if(ihdr->bit_depth == 16) { memcpy(trns_px, &ctx->trns.red, 2); memcpy(trns_px + 2, &ctx->trns.green, 2); memcpy(trns_px + 4, &ctx->trns.blue, 2); } else { trns_px[0] = ctx->trns.red & mask; trns_px[1] = ctx->trns.green & mask; trns_px[2] = ctx->trns.blue & mask; } } } else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) // fmt == SPNG_FMT_GA8 && { if(ihdr->bit_depth == 16) { memcpy(trns_px, &ctx->trns.gray, 2); } else { trns_px[0] = ctx->trns.gray & mask; } } } ctx->decode_flags = f; ctx->state = SPNG_STATE_DECODE_INIT; struct spng_row_info *ri = &ctx->row_info; struct spng_subimage *sub = ctx->subimage; while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++; if(f.interlaced) ri->row_num = adam7_y_start[ri->pass]; unsigned pixel_size = 4; /* SPNG_FMT_RGBA8 */ if(fmt == SPNG_FMT_RGBA16) pixel_size = 8; else if(fmt == SPNG_FMT_RGB8) pixel_size = 3; else if(fmt == SPNG_FMT_G8) pixel_size = 1; else if(fmt == SPNG_FMT_GA8) pixel_size = 2; int i; for(i=ri->pass; i <= ctx->last_pass; i++) { if(!sub[i].scanline_width) continue; if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) sub[i].out_width = sub[i].scanline_width - 1; else sub[i].out_width = (size_t)sub[i].width * pixel_size; if(sub[i].out_width > UINT32_MAX) return decode_err(ctx, SPNG_EOVERFLOW); } /* Read the first filter byte, offsetting all reads by 1 byte. The scanlines will be aligned with the start of the array with the next scanline's filter byte at the end, the last scanline will end up being 1 byte "shorter". */ ret = read_scanline_bytes(ctx, &ri->filter, 1); if(ret) return decode_err(ctx, ret); if(ri->filter > 4) return decode_err(ctx, SPNG_EFILTER); if(flags & SPNG_DECODE_PROGRESSIVE) { return 0; } do { size_t ioffset = ri->row_num * ctx->image_width; ret = spng_decode_row(ctx, (unsigned char*)out + ioffset, ctx->image_width); }while(!ret); if(ret != SPNG_EOI) return decode_err(ctx, ret); return 0; } int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info) { if(ctx == NULL || row_info == NULL || ctx->state < SPNG_STATE_DECODE_INIT) return 1; if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; *row_info = ctx->row_info; return 0; } static int write_chunks_before_idat(spng_ctx *ctx) { if(ctx == NULL) return SPNG_EINTERNAL; if(!ctx->encode_only) return SPNG_EINTERNAL; if(!ctx->stored.ihdr) return SPNG_EINTERNAL; int ret; uint32_t i; size_t length; const struct spng_ihdr *ihdr = &ctx->ihdr; unsigned char *data = ctx->decode_plte.raw; ret = write_data(ctx, spng_signature, 8); if(ret) return ret; write_u32(data, ihdr->width); write_u32(data + 4, ihdr->height); data[8] = ihdr->bit_depth; data[9] = ihdr->color_type; data[10] = ihdr->compression_method; data[11] = ihdr->filter_method; data[12] = ihdr->interlace_method; ret = write_chunk(ctx, type_ihdr, data, 13); if(ret) return ret; if(ctx->stored.chrm) { write_u32(data, ctx->chrm_int.white_point_x); write_u32(data + 4, ctx->chrm_int.white_point_y); write_u32(data + 8, ctx->chrm_int.red_x); write_u32(data + 12, ctx->chrm_int.red_y); write_u32(data + 16, ctx->chrm_int.green_x); write_u32(data + 20, ctx->chrm_int.green_y); write_u32(data + 24, ctx->chrm_int.blue_x); write_u32(data + 28, ctx->chrm_int.blue_y); ret = write_chunk(ctx, type_chrm, data, 32); if(ret) return ret; } if(ctx->stored.gama) { write_u32(data, ctx->gama); ret = write_chunk(ctx, type_gama, data, 4); if(ret) return ret; } if(ctx->stored.iccp) { uLongf dest_len = compressBound((uLong)ctx->iccp.profile_len); Bytef *buf = spng__malloc(ctx, dest_len); if(buf == NULL) return SPNG_EMEM; ret = compress2(buf, &dest_len, (void*)ctx->iccp.profile, (uLong)ctx->iccp.profile_len, Z_DEFAULT_COMPRESSION); if(ret != Z_OK) { spng__free(ctx, buf); return SPNG_EZLIB; } size_t name_len = strlen(ctx->iccp.profile_name); length = name_len + 2; length += dest_len; if(dest_len > length) return SPNG_EOVERFLOW; unsigned char *cdata = NULL; ret = write_header(ctx, type_iccp, length, &cdata); if(ret) { spng__free(ctx, buf); return ret; } memcpy(cdata, ctx->iccp.profile_name, name_len + 1); cdata[name_len + 1] = 0; /* compression method */ memcpy(cdata + name_len + 2, buf, dest_len); spng__free(ctx, buf); ret = finish_chunk(ctx); if(ret) return ret; } if(ctx->stored.sbit) { switch(ctx->ihdr.color_type) { case SPNG_COLOR_TYPE_GRAYSCALE: { length = 1; data[0] = ctx->sbit.grayscale_bits; break; } case SPNG_COLOR_TYPE_TRUECOLOR: case SPNG_COLOR_TYPE_INDEXED: { length = 3; data[0] = ctx->sbit.red_bits; data[1] = ctx->sbit.green_bits; data[2] = ctx->sbit.blue_bits; break; } case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: { length = 2; data[0] = ctx->sbit.grayscale_bits; data[1] = ctx->sbit.alpha_bits; break; } case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: { length = 4; data[0] = ctx->sbit.red_bits; data[1] = ctx->sbit.green_bits; data[2] = ctx->sbit.blue_bits; data[3] = ctx->sbit.alpha_bits; break; } default: return SPNG_EINTERNAL; } ret = write_chunk(ctx, type_sbit, data, length); if(ret) return ret; } if(ctx->stored.srgb) { ret = write_chunk(ctx, type_srgb, &ctx->srgb_rendering_intent, 1); if(ret) return ret; } ret = write_unknown_chunks(ctx, SPNG_AFTER_IHDR); if(ret) return ret; if(ctx->stored.plte) { for(i=0; i < ctx->plte.n_entries; i++) { data[i * 3 + 0] = ctx->plte.entries[i].red; data[i * 3 + 1] = ctx->plte.entries[i].green; data[i * 3 + 2] = ctx->plte.entries[i].blue; } ret = write_chunk(ctx, type_plte, data, ctx->plte.n_entries * 3); if(ret) return ret; } if(ctx->stored.bkgd) { switch(ctx->ihdr.color_type) { case SPNG_COLOR_TYPE_GRAYSCALE: case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: { length = 2; write_u16(data, ctx->bkgd.gray); break; } case SPNG_COLOR_TYPE_TRUECOLOR: case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: { length = 6; write_u16(data, ctx->bkgd.red); write_u16(data + 2, ctx->bkgd.green); write_u16(data + 4, ctx->bkgd.blue); break; } case SPNG_COLOR_TYPE_INDEXED: { length = 1; data[0] = ctx->bkgd.plte_index; break; } default: return SPNG_EINTERNAL; } ret = write_chunk(ctx, type_bkgd, data, length); if(ret) return ret; } if(ctx->stored.hist) { length = ctx->plte.n_entries * 2; for(i=0; i < ctx->plte.n_entries; i++) { write_u16(data + i * 2, ctx->hist.frequency[i]); } ret = write_chunk(ctx, type_hist, data, length); if(ret) return ret; } if(ctx->stored.trns) { if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) { write_u16(data, ctx->trns.gray); ret = write_chunk(ctx, type_trns, data, 2); } else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) { write_u16(data, ctx->trns.red); write_u16(data + 2, ctx->trns.green); write_u16(data + 4, ctx->trns.blue); ret = write_chunk(ctx, type_trns, data, 6); } else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) { ret = write_chunk(ctx, type_trns, ctx->trns.type3_alpha, ctx->trns.n_type3_entries); } if(ret) return ret; } if(ctx->stored.phys) { write_u32(data, ctx->phys.ppu_x); write_u32(data + 4, ctx->phys.ppu_y); data[8] = ctx->phys.unit_specifier; ret = write_chunk(ctx, type_phys, data, 9); if(ret) return ret; } if(ctx->stored.splt) { const struct spng_splt *splt; unsigned char *cdata = NULL; uint32_t k; for(i=0; i < ctx->n_splt; i++) { splt = &ctx->splt_list[i]; size_t name_len = strlen(splt->name); length = name_len + 1; if(splt->sample_depth == 8) length += splt->n_entries * 6 + 1; else if(splt->sample_depth == 16) length += splt->n_entries * 10 + 1; ret = write_header(ctx, type_splt, length, &cdata); if(ret) return ret; memcpy(cdata, splt->name, name_len + 1); cdata += name_len + 2; cdata[-1] = splt->sample_depth; if(splt->sample_depth == 8) { for(k=0; k < splt->n_entries; k++) { cdata[k * 6 + 0] = splt->entries[k].red; cdata[k * 6 + 1] = splt->entries[k].green; cdata[k * 6 + 2] = splt->entries[k].blue; cdata[k * 6 + 3] = splt->entries[k].alpha; write_u16(cdata + k * 6 + 4, splt->entries[k].frequency); } } else if(splt->sample_depth == 16) { for(k=0; k < splt->n_entries; k++) { write_u16(cdata + k * 10 + 0, splt->entries[k].red); write_u16(cdata + k * 10 + 2, splt->entries[k].green); write_u16(cdata + k * 10 + 4, splt->entries[k].blue); write_u16(cdata + k * 10 + 6, splt->entries[k].alpha); write_u16(cdata + k * 10 + 8, splt->entries[k].frequency); } } ret = finish_chunk(ctx); if(ret) return ret; } } if(ctx->stored.time) { write_u16(data, ctx->time.year); data[2] = ctx->time.month; data[3] = ctx->time.day; data[4] = ctx->time.hour; data[5] = ctx->time.minute; data[6] = ctx->time.second; ret = write_chunk(ctx, type_time, data, 7); if(ret) return ret; } if(ctx->stored.text) { unsigned char *cdata = NULL; const struct spng_text2 *text; const uint8_t *text_type_array[4] = { 0, type_text, type_ztxt, type_itxt }; for(i=0; i < ctx->n_text; i++) { text = &ctx->text_list[i]; const uint8_t *text_chunk_type = text_type_array[text->type]; Bytef *compressed_text = NULL; size_t keyword_len = 0; size_t language_tag_len = 0; size_t translated_keyword_len = 0; size_t compressed_length = 0; size_t text_length = 0; keyword_len = strlen(text->keyword); text_length = strlen(text->text); length = keyword_len + 1; if(text->type == SPNG_ZTXT) { length += 1; /* compression method */ } else if(text->type == SPNG_ITXT) { if(!text->language_tag || !text->translated_keyword) return SPNG_EINTERNAL; language_tag_len = strlen(text->language_tag); translated_keyword_len = strlen(text->translated_keyword); length += language_tag_len; if(length < language_tag_len) return SPNG_EOVERFLOW; length += translated_keyword_len; if(length < translated_keyword_len) return SPNG_EOVERFLOW; length += 4; /* compression flag + method + nul for the two strings */ if(length < 4) return SPNG_EOVERFLOW; } if(text->compression_flag) { ret = spng__deflate_init(ctx, &ctx->text_options); if(ret) return ret; z_stream *zstream = &ctx->zstream; uLongf dest_len = deflateBound(zstream, (uLong)text_length); compressed_text = spng__malloc(ctx, dest_len); if(compressed_text == NULL) return SPNG_EMEM; zstream->next_in = (void*)text->text; zstream->avail_in = (uInt)text_length; zstream->next_out = compressed_text; zstream->avail_out = dest_len; ret = deflate(zstream, Z_FINISH); if(ret != Z_STREAM_END) { spng__free(ctx, compressed_text); return SPNG_EZLIB; } compressed_length = zstream->total_out; length += compressed_length; if(length < compressed_length) return SPNG_EOVERFLOW; } else { text_length = strlen(text->text); length += text_length; if(length < text_length) return SPNG_EOVERFLOW; } ret = write_header(ctx, text_chunk_type, length, &cdata); if(ret) { spng__free(ctx, compressed_text); return ret; } memcpy(cdata, text->keyword, keyword_len + 1); cdata += keyword_len + 1; if(text->type == SPNG_ITXT) { cdata[0] = text->compression_flag; cdata[1] = 0; /* compression method */ cdata += 2; memcpy(cdata, text->language_tag, language_tag_len + 1); cdata += language_tag_len + 1; memcpy(cdata, text->translated_keyword, translated_keyword_len + 1); cdata += translated_keyword_len + 1; } else if(text->type == SPNG_ZTXT) { cdata[0] = 0; /* compression method */ cdata++; } if(text->compression_flag) memcpy(cdata, compressed_text, compressed_length); else memcpy(cdata, text->text, text_length); spng__free(ctx, compressed_text); ret = finish_chunk(ctx); if(ret) return ret; } } if(ctx->stored.offs) { write_s32(data, ctx->offs.x); write_s32(data + 4, ctx->offs.y); data[8] = ctx->offs.unit_specifier; ret = write_chunk(ctx, type_offs, data, 9); if(ret) return ret; } if(ctx->stored.exif) { ret = write_chunk(ctx, type_exif, ctx->exif.data, ctx->exif.length); if(ret) return ret; } ret = write_unknown_chunks(ctx, SPNG_AFTER_PLTE); if(ret) return ret; return 0; } static int write_chunks_after_idat(spng_ctx *ctx) { if(ctx == NULL) return SPNG_EINTERNAL; int ret = write_unknown_chunks(ctx, SPNG_AFTER_IDAT); if(ret) return ret; return write_iend(ctx); } /* Compress and write scanline to IDAT stream */ static int write_idat_bytes(spng_ctx *ctx, const void *scanline, size_t len, int flush) { if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL; if(len > UINT_MAX) return SPNG_EINTERNAL; int ret = 0; unsigned char *data = NULL; z_stream *zstream = &ctx->zstream; uint32_t idat_length = SPNG_WRITE_SIZE; zstream->next_in = scanline; zstream->avail_in = (uInt)len; do { ret = deflate(zstream, flush); if(zstream->avail_out == 0) { ret = finish_chunk(ctx); if(ret) return encode_err(ctx, ret); ret = write_header(ctx, type_idat, idat_length, &data); if(ret) return encode_err(ctx, ret); zstream->next_out = data; zstream->avail_out = idat_length; } }while(zstream->avail_in); if(ret != Z_OK) return SPNG_EZLIB; return 0; } static int finish_idat(spng_ctx *ctx) { int ret = 0; unsigned char *data = NULL; z_stream *zstream = &ctx->zstream; uint32_t idat_length = SPNG_WRITE_SIZE; while(ret != Z_STREAM_END) { ret = deflate(zstream, Z_FINISH); if(ret) { if(ret == Z_STREAM_END) break; if(ret != Z_BUF_ERROR) return SPNG_EZLIB; } if(zstream->avail_out == 0) { ret = finish_chunk(ctx); if(ret) return encode_err(ctx, ret); ret = write_header(ctx, type_idat, idat_length, &data); if(ret) return encode_err(ctx, ret); zstream->next_out = data; zstream->avail_out = idat_length; } } uint32_t trimmed_length = idat_length - zstream->avail_out; ret = trim_chunk(ctx, trimmed_length); if(ret) return ret; return finish_chunk(ctx); } static int encode_scanline(spng_ctx *ctx, const void *scanline, size_t len) { if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL; int ret, pass = ctx->row_info.pass; uint8_t filter = 0; struct spng_row_info *ri = &ctx->row_info; const struct spng_subimage *sub = ctx->subimage; struct encode_flags f = ctx->encode_flags; unsigned char *filtered_scanline = ctx->filtered_scanline; size_t scanline_width = sub[pass].scanline_width; if(len < scanline_width - 1) return SPNG_EINTERNAL; /* encode_row() interlaces directly to ctx->scanline */ if(scanline != ctx->scanline) memcpy(ctx->scanline, scanline, scanline_width - 1); if(f.to_bigendian) u16_row_to_bigendian(ctx->scanline, scanline_width - 1); const int requires_previous = f.filter_choice & (SPNG_FILTER_CHOICE_UP | SPNG_FILTER_CHOICE_AVG | SPNG_FILTER_CHOICE_PAETH); /* XXX: exclude 'requires_previous' filters by default for first scanline? */ if(!ri->scanline_idx && requires_previous) { /* prev_scanline is all zeros for the first scanline */ memset(ctx->prev_scanline, 0, scanline_width); } filter = get_best_filter(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, f.filter_choice); if(!filter) filtered_scanline = ctx->scanline; filtered_scanline[-1] = filter; if(filter) { ret = filter_scanline(filtered_scanline, ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, filter); if(ret) return encode_err(ctx, ret); } ret = write_idat_bytes(ctx, filtered_scanline - 1, scanline_width, Z_NO_FLUSH); if(ret) return encode_err(ctx, ret); /* The previous scanline is always unfiltered */ void *t = ctx->prev_scanline; ctx->prev_scanline = ctx->scanline; ctx->scanline = t; ret = update_row_info(ctx); if(ret == SPNG_EOI) { int error = finish_idat(ctx); if(error) encode_err(ctx, error); if(f.finalize) { error = spng_encode_chunks(ctx); if(error) return encode_err(ctx, error); } } return ret; } static int encode_row(spng_ctx *ctx, const void *row, size_t len) { if(ctx == NULL || row == NULL) return SPNG_EINTERNAL; const int pass = ctx->row_info.pass; if(!ctx->ihdr.interlace_method || pass == 6) return encode_scanline(ctx, row, len); uint32_t k; const unsigned pixel_size = ctx->pixel_size; const unsigned bit_depth = ctx->ihdr.bit_depth; if(bit_depth < 8) { const unsigned samples_per_byte = 8 / bit_depth; const uint8_t mask = (1 << bit_depth) - 1; const unsigned initial_shift = 8 - bit_depth; unsigned shift_amount = initial_shift; unsigned char *scanline = ctx->scanline; const unsigned char *row_uc = row; uint8_t sample; memset(scanline, 0, ctx->subimage[pass].scanline_width); for(k=0; k < ctx->subimage[pass].width; k++) { size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass]; sample = row_uc[ioffset / samples_per_byte]; sample = sample >> (initial_shift - ioffset * bit_depth % 8); sample = sample & mask; sample = sample << shift_amount; scanline[0] |= sample; shift_amount -= bit_depth; if(shift_amount > 7) { shift_amount = initial_shift; scanline++; } } return encode_scanline(ctx, ctx->scanline, len); } for(k=0; k < ctx->subimage[pass].width; k++) { size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size; memcpy(ctx->scanline + k * pixel_size, (unsigned char*)row + ioffset, pixel_size); } return encode_scanline(ctx, ctx->scanline, len); } int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len) { if(ctx == NULL || scanline == NULL) return SPNG_EINVAL; if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; if(len < (ctx->subimage[ctx->row_info.pass].scanline_width -1) ) return SPNG_EBUFSIZ; return encode_scanline(ctx, scanline, len); } int spng_encode_row(spng_ctx *ctx, const void *row, size_t len) { if(ctx == NULL || row == NULL) return SPNG_EINVAL; if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; if(len < ctx->image_width) return SPNG_EBUFSIZ; return encode_row(ctx, row, len); } int spng_encode_chunks(spng_ctx *ctx) { if(ctx == NULL) return 1; if(!ctx->state) return SPNG_EBADSTATE; if(ctx->state < SPNG_STATE_OUTPUT) return SPNG_ENODST; if(!ctx->encode_only) return SPNG_ECTXTYPE; int ret = 0; if(ctx->state < SPNG_STATE_FIRST_IDAT) { if(!ctx->stored.ihdr) return SPNG_ENOIHDR; ret = write_chunks_before_idat(ctx); if(ret) return encode_err(ctx, ret); ctx->state = SPNG_STATE_FIRST_IDAT; } else if(ctx->state == SPNG_STATE_FIRST_IDAT) { return 0; } else if(ctx->state == SPNG_STATE_EOI) { ret = write_chunks_after_idat(ctx); if(ret) return encode_err(ctx, ret); ctx->state = SPNG_STATE_IEND; } else return SPNG_EOPSTATE; return 0; } int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags) { if(ctx == NULL) return 1; if(!ctx->state) return SPNG_EBADSTATE; if(!ctx->encode_only) return SPNG_ECTXTYPE; if(!ctx->stored.ihdr) return SPNG_ENOIHDR; if( !(fmt == SPNG_FMT_PNG || fmt == SPNG_FMT_RAW) ) return SPNG_EFMT; int ret = 0; const struct spng_ihdr *ihdr = &ctx->ihdr; struct encode_flags *encode_flags = &ctx->encode_flags; if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED && !ctx->stored.plte) return SPNG_ENOPLTE; ret = calculate_image_width(ihdr, fmt, &ctx->image_width); if(ret) return encode_err(ctx, ret); if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */ else ctx->image_size = ctx->image_width * ihdr->height; if( !(flags & SPNG_ENCODE_PROGRESSIVE) ) { if(img == NULL) return 1; if(!ctx->image_size) return SPNG_EOVERFLOW; if(len != ctx->image_size) return SPNG_EBUFSIZ; } ret = spng_encode_chunks(ctx); if(ret) return encode_err(ctx, ret); ret = calculate_subimages(ctx); if(ret) return encode_err(ctx, ret); if(ihdr->bit_depth < 8) ctx->bytes_per_pixel = 1; else ctx->bytes_per_pixel = num_channels(ihdr) * (ihdr->bit_depth / 8); if(spng__optimize(SPNG_FILTER_CHOICE)) { /* Filtering would make no difference */ if(!ctx->image_options.compression_level) { encode_flags->filter_choice = SPNG_DISABLE_FILTERING; } /* Palette indices and low bit-depth images do not benefit from filtering */ if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED || ihdr->bit_depth < 8) { encode_flags->filter_choice = SPNG_DISABLE_FILTERING; } } /* This is technically the same as disabling filtering */ if(encode_flags->filter_choice == SPNG_FILTER_CHOICE_NONE) { encode_flags->filter_choice = SPNG_DISABLE_FILTERING; } if(!encode_flags->filter_choice && spng__optimize(SPNG_IMG_COMPRESSION_STRATEGY)) { ctx->image_options.strategy = Z_DEFAULT_STRATEGY; } ret = spng__deflate_init(ctx, &ctx->image_options); if(ret) return encode_err(ctx, ret); size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width; scanline_buf_size += 32; if(scanline_buf_size < 32) return SPNG_EOVERFLOW; ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size); ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size); if(ctx->scanline_buf == NULL || ctx->prev_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM); /* Maintain alignment for pixels, filter at [-1] */ ctx->scanline = ctx->scanline_buf + 16; ctx->prev_scanline = ctx->prev_scanline_buf + 16; if(encode_flags->filter_choice) { ctx->filtered_scanline_buf = spng__malloc(ctx, scanline_buf_size); if(ctx->filtered_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM); ctx->filtered_scanline = ctx->filtered_scanline_buf + 16; } struct spng_subimage *sub = ctx->subimage; struct spng_row_info *ri = &ctx->row_info; ctx->fmt = fmt; z_stream *zstream = &ctx->zstream; zstream->avail_out = SPNG_WRITE_SIZE; ret = write_header(ctx, type_idat, zstream->avail_out, &zstream->next_out); if(ret) return encode_err(ctx, ret); if(ihdr->interlace_method) encode_flags->interlace = 1; if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW) ) encode_flags->same_layout = 1; if(ihdr->bit_depth == 16 && fmt != SPNG_FMT_RAW) encode_flags->to_bigendian = 1; if(flags & SPNG_ENCODE_FINALIZE) encode_flags->finalize = 1; while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++; if(encode_flags->interlace) ri->row_num = adam7_y_start[ri->pass]; ctx->pixel_size = 4; /* SPNG_FMT_RGBA8 */ if(fmt == SPNG_FMT_RGBA16) ctx->pixel_size = 8; else if(fmt == SPNG_FMT_RGB8) ctx->pixel_size = 3; else if(fmt == SPNG_FMT_G8) ctx->pixel_size = 1; else if(fmt == SPNG_FMT_GA8) ctx->pixel_size = 2; else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) ctx->pixel_size = ctx->bytes_per_pixel; ctx->state = SPNG_STATE_ENCODE_INIT; if(flags & SPNG_ENCODE_PROGRESSIVE) { encode_flags->progressive = 1; return 0; } do { size_t ioffset = ri->row_num * ctx->image_width; ret = encode_row(ctx, (unsigned char*)img + ioffset, ctx->image_width); }while(!ret); if(ret != SPNG_EOI) return encode_err(ctx, ret); return 0; } spng_ctx *spng_ctx_new(int flags) { struct spng_alloc alloc = { .malloc_fn = malloc, .realloc_fn = realloc, .calloc_fn = calloc, .free_fn = free }; return spng_ctx_new2(&alloc, flags); } spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags) { if(alloc == NULL) return NULL; if(flags != (flags & SPNG__CTX_FLAGS_ALL)) return NULL; if(alloc->malloc_fn == NULL) return NULL; if(alloc->realloc_fn == NULL) return NULL; if(alloc->calloc_fn == NULL) return NULL; if(alloc->free_fn == NULL) return NULL; spng_ctx *ctx = alloc->calloc_fn(1, sizeof(spng_ctx)); if(ctx == NULL) return NULL; ctx->alloc = *alloc; ctx->max_width = spng_u32max; ctx->max_height = spng_u32max; ctx->max_chunk_size = spng_u32max; ctx->chunk_cache_limit = SIZE_MAX; ctx->chunk_count_limit = SPNG_MAX_CHUNK_COUNT; ctx->state = SPNG_STATE_INIT; ctx->crc_action_critical = SPNG_CRC_ERROR; ctx->crc_action_ancillary = SPNG_CRC_DISCARD; const struct spng__zlib_options image_defaults = { .compression_level = Z_DEFAULT_COMPRESSION, .window_bits = 15, .mem_level = 8, .strategy = Z_FILTERED, .data_type = 0 /* Z_BINARY */ }; const struct spng__zlib_options text_defaults = { .compression_level = Z_DEFAULT_COMPRESSION, .window_bits = 15, .mem_level = 8, .strategy = Z_DEFAULT_STRATEGY, .data_type = 1 /* Z_TEXT */ }; ctx->image_options = image_defaults; ctx->text_options = text_defaults; ctx->optimize_option = ~0; ctx->encode_flags.filter_choice = SPNG_FILTER_CHOICE_ALL; ctx->flags = flags; if(flags & SPNG_CTX_ENCODER) ctx->encode_only = 1; return ctx; } void spng_ctx_free(spng_ctx *ctx) { if(ctx == NULL) return; if(ctx->streaming && ctx->stream_buf != NULL) spng__free(ctx, ctx->stream_buf); if(!ctx->user.exif) spng__free(ctx, ctx->exif.data); if(!ctx->user.iccp) spng__free(ctx, ctx->iccp.profile); uint32_t i; if(ctx->splt_list != NULL && !ctx->user.splt) { for(i=0; i < ctx->n_splt; i++) { spng__free(ctx, ctx->splt_list[i].entries); } spng__free(ctx, ctx->splt_list); } if(ctx->text_list != NULL) { for(i=0; i< ctx->n_text; i++) { if(ctx->user.text) break; spng__free(ctx, ctx->text_list[i].keyword); if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text); } spng__free(ctx, ctx->text_list); } if(ctx->chunk_list != NULL && !ctx->user.unknown) { for(i=0; i< ctx->n_chunks; i++) { spng__free(ctx, ctx->chunk_list[i].data); } spng__free(ctx, ctx->chunk_list); } if(ctx->deflate) deflateEnd(&ctx->zstream); else inflateEnd(&ctx->zstream); if(!ctx->user_owns_out_png) spng__free(ctx, ctx->out_png); spng__free(ctx, ctx->gamma_lut16); spng__free(ctx, ctx->row_buf); spng__free(ctx, ctx->scanline_buf); spng__free(ctx, ctx->prev_scanline_buf); spng__free(ctx, ctx->filtered_scanline_buf); spng_free_fn *free_fn = ctx->alloc.free_fn; memset(ctx, 0, sizeof(spng_ctx)); free_fn(ctx); } static int buffer_read_fn(spng_ctx *ctx, void *user, void *data, size_t n) { if(n > ctx->bytes_left) return SPNG_IO_EOF; (void)user; (void)data; ctx->data = ctx->data + ctx->last_read_size; ctx->last_read_size = n; ctx->bytes_left -= n; return 0; } static int file_read_fn(spng_ctx *ctx, void *user, void *data, size_t n) { FILE *file = user; (void)ctx; if(fread(data, n, 1, file) != 1) { if(feof(file)) return SPNG_IO_EOF; else return SPNG_IO_ERROR; } return 0; } static int file_write_fn(spng_ctx *ctx, void *user, void *data, size_t n) { FILE *file = user; (void)ctx; if(fwrite(data, n, 1, file) != 1) return SPNG_IO_ERROR; return 0; } int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size) { if(ctx == NULL || buf == NULL) return 1; if(!ctx->state) return SPNG_EBADSTATE; if(ctx->encode_only) return SPNG_ECTXTYPE; /* not supported */ if(ctx->data != NULL) return SPNG_EBUF_SET; ctx->data = buf; ctx->png_base = buf; ctx->data_size = size; ctx->bytes_left = size; ctx->read_fn = buffer_read_fn; ctx->state = SPNG_STATE_INPUT; return 0; } int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user) { if(ctx == NULL || rw_func == NULL) return 1; if(!ctx->state) return SPNG_EBADSTATE; /* SPNG_STATE_OUTPUT shares the same value */ if(ctx->state >= SPNG_STATE_INPUT) return SPNG_EBUF_SET; if(ctx->encode_only) { if(ctx->out_png != NULL) return SPNG_EBUF_SET; ctx->write_fn = rw_func; ctx->write_ptr = ctx->stream_buf; ctx->state = SPNG_STATE_OUTPUT; } else { ctx->stream_buf = spng__malloc(ctx, SPNG_READ_SIZE); if(ctx->stream_buf == NULL) return SPNG_EMEM; ctx->read_fn = rw_func; ctx->data = ctx->stream_buf; ctx->data_size = SPNG_READ_SIZE; ctx->state = SPNG_STATE_INPUT; } ctx->stream_user_ptr = user; ctx->streaming = 1; return 0; } int spng_set_png_file(spng_ctx *ctx, FILE *file) { if(file == NULL) return 1; if(ctx->encode_only) return spng_set_png_stream(ctx, file_write_fn, file); return spng_set_png_stream(ctx, file_read_fn, file); } void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error) { int tmp = 0; error = error ? error : &tmp; *error = 0; if(ctx == NULL || !len) *error = SPNG_EINVAL; if(*error) return NULL; if(!ctx->encode_only) *error = SPNG_ECTXTYPE; else if(!ctx->state) *error = SPNG_EBADSTATE; else if(!ctx->internal_buffer) *error = SPNG_EOPSTATE; else if(ctx->state < SPNG_STATE_EOI) *error = SPNG_EOPSTATE; else if(ctx->state != SPNG_STATE_IEND) *error = SPNG_ENOTFINAL; if(*error) return NULL; ctx->user_owns_out_png = 1; *len = ctx->bytes_encoded; return ctx->out_png; } int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height) { if(ctx == NULL) return 1; if(width > spng_u32max || height > spng_u32max) return 1; ctx->max_width = width; ctx->max_height = height; return 0; } int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height) { if(ctx == NULL || width == NULL || height == NULL) return 1; *width = ctx->max_width; *height = ctx->max_height; return 0; } int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_limit) { if(ctx == NULL || chunk_size > spng_u32max || chunk_size > cache_limit) return 1; ctx->max_chunk_size = chunk_size; ctx->chunk_cache_limit = cache_limit; return 0; } int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_limit) { if(ctx == NULL || chunk_size == NULL || cache_limit == NULL) return 1; *chunk_size = ctx->max_chunk_size; *cache_limit = ctx->chunk_cache_limit; return 0; } int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary) { if(ctx == NULL) return 1; if(ctx->encode_only) return SPNG_ECTXTYPE; if(critical > 2 || critical < 0) return 1; if(ancillary > 2 || ancillary < 0) return 1; if(critical == SPNG_CRC_DISCARD) return 1; ctx->crc_action_critical = critical; ctx->crc_action_ancillary = ancillary; return 0; } int spng_set_option(spng_ctx *ctx, enum spng_option option, int value) { if(ctx == NULL) return 1; if(!ctx->state) return SPNG_EBADSTATE; switch(option) { case SPNG_KEEP_UNKNOWN_CHUNKS: { ctx->keep_unknown = value ? 1 : 0; break; } case SPNG_IMG_COMPRESSION_LEVEL: { ctx->image_options.compression_level = value; break; } case SPNG_IMG_WINDOW_BITS: { ctx->image_options.window_bits = value; break; } case SPNG_IMG_MEM_LEVEL: { ctx->image_options.mem_level = value; break; } case SPNG_IMG_COMPRESSION_STRATEGY: { ctx->image_options.strategy = value; break; } case SPNG_TEXT_COMPRESSION_LEVEL: { ctx->text_options.compression_level = value; break; } case SPNG_TEXT_WINDOW_BITS: { ctx->text_options.window_bits = value; break; } case SPNG_TEXT_MEM_LEVEL: { ctx->text_options.mem_level = value; break; } case SPNG_TEXT_COMPRESSION_STRATEGY: { ctx->text_options.strategy = value; break; } case SPNG_FILTER_CHOICE: { if(value & ~SPNG_FILTER_CHOICE_ALL) return 1; ctx->encode_flags.filter_choice = value; break; } case SPNG_CHUNK_COUNT_LIMIT: { if(value < 0) return 1; if(value > (int)ctx->chunk_count_total) return 1; ctx->chunk_count_limit = value; break; } case SPNG_ENCODE_TO_BUFFER: { if(value < 0) return 1; if(!ctx->encode_only) return SPNG_ECTXTYPE; if(ctx->state >= SPNG_STATE_OUTPUT) return SPNG_EOPSTATE; if(!value) break; ctx->internal_buffer = 1; ctx->state = SPNG_STATE_OUTPUT; break; } default: return 1; } /* Option can no longer be overriden by the library */ if(option < 32) ctx->optimize_option &= ~(1 << option); return 0; } int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value) { if(ctx == NULL || value == NULL) return 1; if(!ctx->state) return SPNG_EBADSTATE; switch(option) { case SPNG_KEEP_UNKNOWN_CHUNKS: { *value = ctx->keep_unknown; break; } case SPNG_IMG_COMPRESSION_LEVEL: { *value = ctx->image_options.compression_level; break; } case SPNG_IMG_WINDOW_BITS: { *value = ctx->image_options.window_bits; break; } case SPNG_IMG_MEM_LEVEL: { *value = ctx->image_options.mem_level; break; } case SPNG_IMG_COMPRESSION_STRATEGY: { *value = ctx->image_options.strategy; break; } case SPNG_TEXT_COMPRESSION_LEVEL: { *value = ctx->text_options.compression_level; break; } case SPNG_TEXT_WINDOW_BITS: { *value = ctx->text_options.window_bits; break; } case SPNG_TEXT_MEM_LEVEL: { *value = ctx->text_options.mem_level; break; } case SPNG_TEXT_COMPRESSION_STRATEGY: { *value = ctx->text_options.strategy; break; } case SPNG_FILTER_CHOICE: { *value = ctx->encode_flags.filter_choice; break; } case SPNG_CHUNK_COUNT_LIMIT: { *value = ctx->chunk_count_limit; break; } case SPNG_ENCODE_TO_BUFFER: { if(ctx->internal_buffer) *value = 1; else *value = 0; break; } default: return 1; } return 0; } int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len) { if(ctx == NULL || len == NULL) return 1; int ret = read_chunks(ctx, 1); if(ret) return ret; ret = check_decode_fmt(&ctx->ihdr, fmt); if(ret) return ret; return calculate_image_size(&ctx->ihdr, fmt, len); } int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr) { if(ctx == NULL) return 1; int ret = read_chunks(ctx, 1); if(ret) return ret; if(ihdr == NULL) return 1; *ihdr = ctx->ihdr; return 0; } int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte) { SPNG_GET_CHUNK_BOILERPLATE(plte); *plte = ctx->plte; return 0; } int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns) { SPNG_GET_CHUNK_BOILERPLATE(trns); *trns = ctx->trns; return 0; } int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm) { SPNG_GET_CHUNK_BOILERPLATE(chrm); chrm->white_point_x = (double)ctx->chrm_int.white_point_x / 100000.0; chrm->white_point_y = (double)ctx->chrm_int.white_point_y / 100000.0; chrm->red_x = (double)ctx->chrm_int.red_x / 100000.0; chrm->red_y = (double)ctx->chrm_int.red_y / 100000.0; chrm->blue_y = (double)ctx->chrm_int.blue_y / 100000.0; chrm->blue_x = (double)ctx->chrm_int.blue_x / 100000.0; chrm->green_x = (double)ctx->chrm_int.green_x / 100000.0; chrm->green_y = (double)ctx->chrm_int.green_y / 100000.0; return 0; } int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm) { SPNG_GET_CHUNK_BOILERPLATE(chrm); *chrm = ctx->chrm_int; return 0; } int spng_get_gama(spng_ctx *ctx, double *gamma) { double *gama = gamma; SPNG_GET_CHUNK_BOILERPLATE(gama); *gama = (double)ctx->gama / 100000.0; return 0; } int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int) { uint32_t *gama = gama_int; SPNG_GET_CHUNK_BOILERPLATE(gama); *gama_int = ctx->gama; return 0; } int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp) { SPNG_GET_CHUNK_BOILERPLATE(iccp); *iccp = ctx->iccp; return 0; } int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit) { SPNG_GET_CHUNK_BOILERPLATE(sbit); *sbit = ctx->sbit; return 0; } int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent) { uint8_t *srgb = rendering_intent; SPNG_GET_CHUNK_BOILERPLATE(srgb); *srgb = ctx->srgb_rendering_intent; return 0; } int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text) { if(ctx == NULL) return 1; int ret = read_chunks(ctx, 0); if(ret) return ret; if(!ctx->stored.text) return SPNG_ECHUNKAVAIL; if(n_text == NULL) return 1; if(text == NULL) { *n_text = ctx->n_text; return 0; } if(*n_text < ctx->n_text) return 1; uint32_t i; for(i=0; i< ctx->n_text; i++) { text[i].type = ctx->text_list[i].type; memcpy(&text[i].keyword, ctx->text_list[i].keyword, strlen(ctx->text_list[i].keyword) + 1); text[i].compression_method = 0; text[i].compression_flag = ctx->text_list[i].compression_flag; text[i].language_tag = ctx->text_list[i].language_tag; text[i].translated_keyword = ctx->text_list[i].translated_keyword; text[i].length = ctx->text_list[i].text_length; text[i].text = ctx->text_list[i].text; } return ret; } int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd) { SPNG_GET_CHUNK_BOILERPLATE(bkgd); *bkgd = ctx->bkgd; return 0; } int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist) { SPNG_GET_CHUNK_BOILERPLATE(hist); *hist = ctx->hist; return 0; } int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys) { SPNG_GET_CHUNK_BOILERPLATE(phys); *phys = ctx->phys; return 0; } int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt) { if(ctx == NULL) return 1; int ret = read_chunks(ctx, 0); if(ret) return ret; if(!ctx->stored.splt) return SPNG_ECHUNKAVAIL; if(n_splt == NULL) return 1; if(splt == NULL) { *n_splt = ctx->n_splt; return 0; } if(*n_splt < ctx->n_splt) return 1; memcpy(splt, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt)); return 0; } int spng_get_time(spng_ctx *ctx, struct spng_time *time) { SPNG_GET_CHUNK_BOILERPLATE(time); *time = ctx->time; return 0; } int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks) { if(ctx == NULL) return 1; int ret = read_chunks(ctx, 0); if(ret) return ret; if(!ctx->stored.unknown) return SPNG_ECHUNKAVAIL; if(n_chunks == NULL) return 1; if(chunks == NULL) { *n_chunks = ctx->n_chunks; return 0; } if(*n_chunks < ctx->n_chunks) return 1; memcpy(chunks, ctx->chunk_list, sizeof(struct spng_unknown_chunk)); return 0; } int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs) { SPNG_GET_CHUNK_BOILERPLATE(offs); *offs = ctx->offs; return 0; } int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif) { SPNG_GET_CHUNK_BOILERPLATE(exif); *exif = ctx->exif; return 0; } int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr) { SPNG_SET_CHUNK_BOILERPLATE(ihdr); if(ctx->stored.ihdr) return 1; ret = check_ihdr(ihdr, ctx->max_width, ctx->max_height); if(ret) return ret; ctx->ihdr = *ihdr; ctx->stored.ihdr = 1; ctx->user.ihdr = 1; return 0; } int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte) { SPNG_SET_CHUNK_BOILERPLATE(plte); if(!ctx->stored.ihdr) return 1; if(check_plte(plte, &ctx->ihdr)) return 1; ctx->plte.n_entries = plte->n_entries; memcpy(ctx->plte.entries, plte->entries, plte->n_entries * sizeof(struct spng_plte_entry)); ctx->stored.plte = 1; ctx->user.plte = 1; return 0; } int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns) { SPNG_SET_CHUNK_BOILERPLATE(trns); if(!ctx->stored.ihdr) return SPNG_ENOIHDR; if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) { ctx->trns.gray = trns->gray; } else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) { ctx->trns.red = trns->red; ctx->trns.green = trns->green; ctx->trns.blue = trns->blue; } else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) { if(!ctx->stored.plte) return SPNG_ETRNS_NO_PLTE; if(trns->n_type3_entries > ctx->plte.n_entries) return 1; ctx->trns.n_type3_entries = trns->n_type3_entries; memcpy(ctx->trns.type3_alpha, trns->type3_alpha, trns->n_type3_entries); } else return SPNG_ETRNS_COLOR_TYPE; ctx->stored.trns = 1; ctx->user.trns = 1; return 0; } int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm) { SPNG_SET_CHUNK_BOILERPLATE(chrm); struct spng_chrm_int chrm_int; chrm_int.white_point_x = (uint32_t)(chrm->white_point_x * 100000.0); chrm_int.white_point_y = (uint32_t)(chrm->white_point_y * 100000.0); chrm_int.red_x = (uint32_t)(chrm->red_x * 100000.0); chrm_int.red_y = (uint32_t)(chrm->red_y * 100000.0); chrm_int.green_x = (uint32_t)(chrm->green_x * 100000.0); chrm_int.green_y = (uint32_t)(chrm->green_y * 100000.0); chrm_int.blue_x = (uint32_t)(chrm->blue_x * 100000.0); chrm_int.blue_y = (uint32_t)(chrm->blue_y * 100000.0); if(check_chrm_int(&chrm_int)) return SPNG_ECHRM; ctx->chrm_int = chrm_int; ctx->stored.chrm = 1; ctx->user.chrm = 1; return 0; } int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int) { SPNG_SET_CHUNK_BOILERPLATE(chrm_int); if(check_chrm_int(chrm_int)) return SPNG_ECHRM; ctx->chrm_int = *chrm_int; ctx->stored.chrm = 1; ctx->user.chrm = 1; return 0; } int spng_set_gama(spng_ctx *ctx, double gamma) { SPNG_SET_CHUNK_BOILERPLATE(ctx); uint32_t gama = gamma * 100000.0; if(!gama) return 1; if(gama > spng_u32max) return 1; ctx->gama = gama; ctx->stored.gama = 1; ctx->user.gama = 1; return 0; } int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma) { SPNG_SET_CHUNK_BOILERPLATE(ctx); if(!gamma) return 1; if(gamma > spng_u32max) return 1; ctx->gama = gamma; ctx->stored.gama = 1; ctx->user.gama = 1; return 0; } int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp) { SPNG_SET_CHUNK_BOILERPLATE(iccp); if(check_png_keyword(iccp->profile_name)) return SPNG_EICCP_NAME; if(!iccp->profile_len) return SPNG_ECHUNK_SIZE; if(iccp->profile_len > spng_u32max) return SPNG_ECHUNK_STDLEN; if(ctx->iccp.profile && !ctx->user.iccp) spng__free(ctx, ctx->iccp.profile); ctx->iccp = *iccp; ctx->stored.iccp = 1; ctx->user.iccp = 1; return 0; } int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit) { SPNG_SET_CHUNK_BOILERPLATE(sbit); if(check_sbit(sbit, &ctx->ihdr)) return 1; if(!ctx->stored.ihdr) return 1; ctx->sbit = *sbit; ctx->stored.sbit = 1; ctx->user.sbit = 1; return 0; } int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent) { SPNG_SET_CHUNK_BOILERPLATE(ctx); if(rendering_intent > 3) return 1; ctx->srgb_rendering_intent = rendering_intent; ctx->stored.srgb = 1; ctx->user.srgb = 1; return 0; } int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text) { if(!n_text) return 1; SPNG_SET_CHUNK_BOILERPLATE(text); uint32_t i; for(i=0; i < n_text; i++) { if(check_png_keyword(text[i].keyword)) return SPNG_ETEXT_KEYWORD; if(!text[i].length) return 1; if(text[i].length > UINT_MAX) return 1; if(text[i].text == NULL) return 1; if(text[i].type == SPNG_TEXT) { if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1; } else if(text[i].type == SPNG_ZTXT) { if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1; if(text[i].compression_method != 0) return SPNG_EZTXT_COMPRESSION_METHOD; } else if(text[i].type == SPNG_ITXT) { if(text[i].compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG; if(text[i].compression_method != 0) return SPNG_EITXT_COMPRESSION_METHOD; if(text[i].language_tag == NULL) return SPNG_EITXT_LANG_TAG; if(text[i].translated_keyword == NULL) return SPNG_EITXT_TRANSLATED_KEY; } else return 1; } struct spng_text2 *text_list = spng__calloc(ctx, sizeof(struct spng_text2), n_text); if(!text_list) return SPNG_EMEM; if(ctx->text_list != NULL) { for(i=0; i < ctx->n_text; i++) { if(ctx->user.text) break; spng__free(ctx, ctx->text_list[i].keyword); if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text); } spng__free(ctx, ctx->text_list); } for(i=0; i < n_text; i++) { text_list[i].type = text[i].type; /* Prevent issues with spng_text.keyword[80] going out of scope */ text_list[i].keyword = text_list[i].user_keyword_storage; memcpy(text_list[i].user_keyword_storage, text[i].keyword, strlen(text[i].keyword)); text_list[i].text = text[i].text; text_list[i].text_length = text[i].length; if(text[i].type == SPNG_ZTXT) { text_list[i].compression_flag = 1; } else if(text[i].type == SPNG_ITXT) { text_list[i].compression_flag = text[i].compression_flag; text_list[i].language_tag = text[i].language_tag; text_list[i].translated_keyword = text[i].translated_keyword; } } ctx->text_list = text_list; ctx->n_text = n_text; ctx->stored.text = 1; ctx->user.text = 1; return 0; } int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd) { SPNG_SET_CHUNK_BOILERPLATE(bkgd); if(!ctx->stored.ihdr) return 1; if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4) { ctx->bkgd.gray = bkgd->gray; } else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6) { ctx->bkgd.red = bkgd->red; ctx->bkgd.green = bkgd->green; ctx->bkgd.blue = bkgd->blue; } else if(ctx->ihdr.color_type == 3) { if(!ctx->stored.plte) return SPNG_EBKGD_NO_PLTE; if(bkgd->plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX; ctx->bkgd.plte_index = bkgd->plte_index; } ctx->stored.bkgd = 1; ctx->user.bkgd = 1; return 0; } int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist) { SPNG_SET_CHUNK_BOILERPLATE(hist); if(!ctx->stored.plte) return SPNG_EHIST_NO_PLTE; ctx->hist = *hist; ctx->stored.hist = 1; ctx->user.hist = 1; return 0; } int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys) { SPNG_SET_CHUNK_BOILERPLATE(phys); if(check_phys(phys)) return SPNG_EPHYS; ctx->phys = *phys; ctx->stored.phys = 1; ctx->user.phys = 1; return 0; } int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt) { if(!n_splt) return 1; SPNG_SET_CHUNK_BOILERPLATE(splt); uint32_t i; for(i=0; i < n_splt; i++) { if(check_png_keyword(splt[i].name)) return SPNG_ESPLT_NAME; if( !(splt[i].sample_depth == 8 || splt[i].sample_depth == 16) ) return SPNG_ESPLT_DEPTH; } if(ctx->stored.splt && !ctx->user.splt) { for(i=0; i < ctx->n_splt; i++) { if(ctx->splt_list[i].entries != NULL) spng__free(ctx, ctx->splt_list[i].entries); } spng__free(ctx, ctx->splt_list); } ctx->splt_list = splt; ctx->n_splt = n_splt; ctx->stored.splt = 1; ctx->user.splt = 1; return 0; } int spng_set_time(spng_ctx *ctx, struct spng_time *time) { SPNG_SET_CHUNK_BOILERPLATE(time); if(check_time(time)) return SPNG_ETIME; ctx->time = *time; ctx->stored.time = 1; ctx->user.time = 1; return 0; } int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks) { if(!n_chunks) return 1; SPNG_SET_CHUNK_BOILERPLATE(chunks); uint32_t i; for(i=0; i < n_chunks; i++) { if(chunks[i].length > spng_u32max) return SPNG_ECHUNK_STDLEN; if(chunks[i].length && chunks[i].data == NULL) return 1; switch(chunks[i].location) { case SPNG_AFTER_IHDR: case SPNG_AFTER_PLTE: case SPNG_AFTER_IDAT: break; default: return SPNG_ECHUNK_POS; } } if(ctx->stored.unknown && !ctx->user.unknown) { for(i=0; i < ctx->n_chunks; i++) { spng__free(ctx, ctx->chunk_list[i].data); } spng__free(ctx, ctx->chunk_list); } ctx->chunk_list = chunks; ctx->n_chunks = n_chunks; ctx->stored.unknown = 1; ctx->user.unknown = 1; return 0; } int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs) { SPNG_SET_CHUNK_BOILERPLATE(offs); if(check_offs(offs)) return SPNG_EOFFS; ctx->offs = *offs; ctx->stored.offs = 1; ctx->user.offs = 1; return 0; } int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif) { SPNG_SET_CHUNK_BOILERPLATE(exif); if(check_exif(exif)) return SPNG_EEXIF; if(ctx->exif.data != NULL && !ctx->user.exif) spng__free(ctx, ctx->exif.data); ctx->exif = *exif; ctx->stored.exif = 1; ctx->user.exif = 1; return 0; } const char *spng_strerror(int err) { switch(err) { case SPNG_IO_EOF: return "end of stream"; case SPNG_IO_ERROR: return "stream error"; case SPNG_OK: return "success"; case SPNG_EINVAL: return "invalid argument"; case SPNG_EMEM: return "out of memory"; case SPNG_EOVERFLOW: return "arithmetic overflow"; case SPNG_ESIGNATURE: return "invalid signature"; case SPNG_EWIDTH: return "invalid image width"; case SPNG_EHEIGHT: return "invalid image height"; case SPNG_EUSER_WIDTH: return "image width exceeds user limit"; case SPNG_EUSER_HEIGHT: return "image height exceeds user limit"; case SPNG_EBIT_DEPTH: return "invalid bit depth"; case SPNG_ECOLOR_TYPE: return "invalid color type"; case SPNG_ECOMPRESSION_METHOD: return "invalid compression method"; case SPNG_EFILTER_METHOD: return "invalid filter method"; case SPNG_EINTERLACE_METHOD: return "invalid interlace method"; case SPNG_EIHDR_SIZE: return "invalid IHDR chunk size"; case SPNG_ENOIHDR: return "missing IHDR chunk"; case SPNG_ECHUNK_POS: return "invalid chunk position"; case SPNG_ECHUNK_SIZE: return "invalid chunk length"; case SPNG_ECHUNK_CRC: return "invalid chunk checksum"; case SPNG_ECHUNK_TYPE: return "invalid chunk type"; case SPNG_ECHUNK_UNKNOWN_CRITICAL: return "unknown critical chunk"; case SPNG_EDUP_PLTE: return "duplicate PLTE chunk"; case SPNG_EDUP_CHRM: return "duplicate cHRM chunk"; case SPNG_EDUP_GAMA: return "duplicate gAMA chunk"; case SPNG_EDUP_ICCP: return "duplicate iCCP chunk"; case SPNG_EDUP_SBIT: return "duplicate sBIT chunk"; case SPNG_EDUP_SRGB: return "duplicate sRGB chunk"; case SPNG_EDUP_BKGD: return "duplicate bKGD chunk"; case SPNG_EDUP_HIST: return "duplicate hIST chunk"; case SPNG_EDUP_TRNS: return "duplicate tRNS chunk"; case SPNG_EDUP_PHYS: return "duplicate pHYs chunk"; case SPNG_EDUP_TIME: return "duplicate tIME chunk"; case SPNG_EDUP_OFFS: return "duplicate oFFs chunk"; case SPNG_EDUP_EXIF: return "duplicate eXIf chunk"; case SPNG_ECHRM: return "invalid cHRM chunk"; case SPNG_EPLTE_IDX: return "invalid palette (PLTE) index"; case SPNG_ETRNS_COLOR_TYPE: return "tRNS chunk with incompatible color type"; case SPNG_ETRNS_NO_PLTE: return "missing palette (PLTE) for tRNS chunk"; case SPNG_EGAMA: return "invalid gAMA chunk"; case SPNG_EICCP_NAME: return "invalid iCCP profile name"; case SPNG_EICCP_COMPRESSION_METHOD: return "invalid iCCP compression method"; case SPNG_ESBIT: return "invalid sBIT chunk"; case SPNG_ESRGB: return "invalid sRGB chunk"; case SPNG_ETEXT: return "invalid tEXt chunk"; case SPNG_ETEXT_KEYWORD: return "invalid tEXt keyword"; case SPNG_EZTXT: return "invalid zTXt chunk"; case SPNG_EZTXT_COMPRESSION_METHOD: return "invalid zTXt compression method"; case SPNG_EITXT: return "invalid iTXt chunk"; case SPNG_EITXT_COMPRESSION_FLAG: return "invalid iTXt compression flag"; case SPNG_EITXT_COMPRESSION_METHOD: return "invalid iTXt compression method"; case SPNG_EITXT_LANG_TAG: return "invalid iTXt language tag"; case SPNG_EITXT_TRANSLATED_KEY: return "invalid iTXt translated key"; case SPNG_EBKGD_NO_PLTE: return "missing palette for bKGD chunk"; case SPNG_EBKGD_PLTE_IDX: return "invalid palette index for bKGD chunk"; case SPNG_EHIST_NO_PLTE: return "missing palette for hIST chunk"; case SPNG_EPHYS: return "invalid pHYs chunk"; case SPNG_ESPLT_NAME: return "invalid suggested palette name"; case SPNG_ESPLT_DUP_NAME: return "duplicate suggested palette (sPLT) name"; case SPNG_ESPLT_DEPTH: return "invalid suggested palette (sPLT) sample depth"; case SPNG_ETIME: return "invalid tIME chunk"; case SPNG_EOFFS: return "invalid oFFs chunk"; case SPNG_EEXIF: return "invalid eXIf chunk"; case SPNG_EIDAT_TOO_SHORT: return "IDAT stream too short"; case SPNG_EIDAT_STREAM: return "IDAT stream error"; case SPNG_EZLIB: return "zlib error"; case SPNG_EFILTER: return "invalid scanline filter"; case SPNG_EBUFSIZ: return "invalid buffer size"; case SPNG_EIO: return "i/o error"; case SPNG_EOF: return "end of file"; case SPNG_EBUF_SET: return "buffer already set"; case SPNG_EBADSTATE: return "non-recoverable state"; case SPNG_EFMT: return "invalid format"; case SPNG_EFLAGS: return "invalid flags"; case SPNG_ECHUNKAVAIL: return "chunk not available"; case SPNG_ENCODE_ONLY: return "encode only context"; case SPNG_EOI: return "reached end-of-image state"; case SPNG_ENOPLTE: return "missing PLTE for indexed image"; case SPNG_ECHUNK_LIMITS: return "reached chunk/cache limits"; case SPNG_EZLIB_INIT: return "zlib init error"; case SPNG_ECHUNK_STDLEN: return "chunk exceeds maximum standard length"; case SPNG_EINTERNAL: return "internal error"; case SPNG_ECTXTYPE: return "invalid operation for context type"; case SPNG_ENOSRC: return "source PNG not set"; case SPNG_ENODST: return "PNG output not set"; case SPNG_EOPSTATE: return "invalid operation for state"; case SPNG_ENOTFINAL: return "PNG not finalized"; default: return "unknown error"; } } const char *spng_version_string(void) { return SPNG_VERSION_STRING; } #if defined(_MSC_VER) #pragma warning(pop) #endif /* The following SIMD optimizations are derived from libpng source code. */ /* * PNG Reference Library License version 2 * * Copyright (c) 1995-2019 The PNG Reference Library Authors. * Copyright (c) 2018-2019 Cosmin Truta. * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * Copyright (c) 1996-1997 Andreas Dilger. * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. * * The software is supplied "as is", without warranty of any kind, * express or implied, including, without limitation, the warranties * of merchantability, fitness for a particular purpose, title, and * non-infringement. In no event shall the Copyright owners, or * anyone distributing the software, be liable for any damages or * other liability, whether in contract, tort or otherwise, arising * from, out of, or in connection with the software, or the use or * other dealings in the software, even if advised of the possibility * of such damage. * * Permission is hereby granted to use, copy, modify, and distribute * this software, or portions hereof, for any purpose, without fee, * subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you * must not claim that you wrote the original software. If you * use this software in a product, an acknowledgment in the product * documentation would be appreciated, but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This Copyright notice may not be removed or altered from any * source or altered source distribution. */ #if defined(SPNG_X86) #ifndef SPNG_SSE #define SPNG_SSE 1 #endif #if defined(__GNUC__) && !defined(__clang__) #if SPNG_SSE == 3 #pragma GCC target("ssse3") #elif SPNG_SSE == 4 #pragma GCC target("sse4.1") #else #pragma GCC target("sse2") #endif #endif /* SSE2 optimised filter functions * Derived from filter_neon_intrinsics.c * * Copyright (c) 2018 Cosmin Truta * Copyright (c) 2016-2017 Glenn Randers-Pehrson * Written by Mike Klein and Matt Sarett * Derived from arm/filter_neon_intrinsics.c * * This code is derived from libpng source code. * For conditions of distribution and use, see the disclaimer * and license above. */ #include #include #include /* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d). * They're positioned like this: * prev: c b * row: a d * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be * whichever of a, b, or c is closest to p=a+b-c. */ static __m128i load4(const void* p) { int tmp; memcpy(&tmp, p, sizeof(tmp)); return _mm_cvtsi32_si128(tmp); } static void store4(void* p, __m128i v) { int tmp = _mm_cvtsi128_si32(v); memcpy(p, &tmp, sizeof(int)); } static __m128i load3(const void* p) { uint32_t tmp = 0; memcpy(&tmp, p, 3); return _mm_cvtsi32_si128(tmp); } static void store3(void* p, __m128i v) { int tmp = _mm_cvtsi128_si32(v); memcpy(p, &tmp, 3); } static void defilter_sub3(size_t rowbytes, unsigned char *row) { /* The Sub filter predicts each pixel as the previous pixel, a. * There is no pixel to the left of the first pixel. It's encoded directly. * That works with our main loop if we just say that left pixel was zero. */ size_t rb = rowbytes; __m128i a, d = _mm_setzero_si128(); while(rb >= 4) { a = d; d = load4(row); d = _mm_add_epi8(d, a); store3(row, d); row += 3; rb -= 3; } if(rb > 0) { a = d; d = load3(row); d = _mm_add_epi8(d, a); store3(row, d); } } static void defilter_sub4(size_t rowbytes, unsigned char *row) { /* The Sub filter predicts each pixel as the previous pixel, a. * There is no pixel to the left of the first pixel. It's encoded directly. * That works with our main loop if we just say that left pixel was zero. */ size_t rb = rowbytes+4; __m128i a, d = _mm_setzero_si128(); while(rb > 4) { a = d; d = load4(row); d = _mm_add_epi8(d, a); store4(row, d); row += 4; rb -= 4; } } static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev) { /* The Avg filter predicts each pixel as the (truncated) average of a and b. * There's no pixel to the left of the first pixel. Luckily, it's * predicted to be half of the pixel above it. So again, this works * perfectly with our loop if we make sure a starts at zero. */ size_t rb = rowbytes; const __m128i zero = _mm_setzero_si128(); __m128i b; __m128i a, d = zero; while(rb >= 4) { __m128i avg; b = load4(prev); a = d; d = load4(row ); /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ avg = _mm_avg_epu8(a,b); /* ...but we can fix it up by subtracting off 1 if it rounded up. */ avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b), _mm_set1_epi8(1))); d = _mm_add_epi8(d, avg); store3(row, d); prev += 3; row += 3; rb -= 3; } if(rb > 0) { __m128i avg; b = load3(prev); a = d; d = load3(row ); /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ avg = _mm_avg_epu8(a, b); /* ...but we can fix it up by subtracting off 1 if it rounded up. */ avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b), _mm_set1_epi8(1))); d = _mm_add_epi8(d, avg); store3(row, d); } } static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev) { /* The Avg filter predicts each pixel as the (truncated) average of a and b. * There's no pixel to the left of the first pixel. Luckily, it's * predicted to be half of the pixel above it. So again, this works * perfectly with our loop if we make sure a starts at zero. */ size_t rb = rowbytes+4; const __m128i zero = _mm_setzero_si128(); __m128i b; __m128i a, d = zero; while(rb > 4) { __m128i avg; b = load4(prev); a = d; d = load4(row ); /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ avg = _mm_avg_epu8(a,b); /* ...but we can fix it up by subtracting off 1 if it rounded up. */ avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b), _mm_set1_epi8(1))); d = _mm_add_epi8(d, avg); store4(row, d); prev += 4; row += 4; rb -= 4; } } /* Returns |x| for 16-bit lanes. */ #if (SPNG_SSE >= 3) && !defined(_MSC_VER) __attribute__((target("ssse3"))) #endif static __m128i abs_i16(__m128i x) { #if SPNG_SSE >= 3 return _mm_abs_epi16(x); #else /* Read this all as, return x<0 ? -x : x. * To negate two's complement, you flip all the bits then add 1. */ __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128()); /* Flip negative lanes. */ x = _mm_xor_si128(x, is_negative); /* +1 to negative lanes, else +0. */ x = _mm_sub_epi16(x, is_negative); return x; #endif } /* Bytewise c ? t : e. */ static __m128i if_then_else(__m128i c, __m128i t, __m128i e) { #if SPNG_SSE >= 4 return _mm_blendv_epi8(e, t, c); #else return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e)); #endif } static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev) { /* Paeth tries to predict pixel d using the pixel to the left of it, a, * and two pixels from the previous row, b and c: * prev: c b * row: a d * The Paeth function predicts d to be whichever of a, b, or c is nearest to * p=a+b-c. * * The first pixel has no left context, and so uses an Up filter, p = b. * This works naturally with our main loop's p = a+b-c if we force a and c * to zero. * Here we zero b and d, which become c and a respectively at the start of * the loop. */ size_t rb = rowbytes; const __m128i zero = _mm_setzero_si128(); __m128i c, b = zero, a, d = zero; while(rb >= 4) { /* It's easiest to do this math (particularly, deal with pc) with 16-bit * intermediates. */ __m128i pa,pb,pc,smallest,nearest; c = b; b = _mm_unpacklo_epi8(load4(prev), zero); a = d; d = _mm_unpacklo_epi8(load4(row ), zero); /* (p-a) == (a+b-c - a) == (b-c) */ pa = _mm_sub_epi16(b, c); /* (p-b) == (a+b-c - b) == (a-c) */ pb = _mm_sub_epi16(a, c); /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ pc = _mm_add_epi16(pa, pb); pa = abs_i16(pa); /* |p-a| */ pb = abs_i16(pb); /* |p-b| */ pc = abs_i16(pc); /* |p-c| */ smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); /* Paeth breaks ties favoring a over b over c. */ nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c)); /* Note `_epi8`: we need addition to wrap modulo 255. */ d = _mm_add_epi8(d, nearest); store3(row, _mm_packus_epi16(d, d)); prev += 3; row += 3; rb -= 3; } if(rb > 0) { /* It's easiest to do this math (particularly, deal with pc) with 16-bit * intermediates. */ __m128i pa, pb, pc, smallest, nearest; c = b; b = _mm_unpacklo_epi8(load3(prev), zero); a = d; d = _mm_unpacklo_epi8(load3(row ), zero); /* (p-a) == (a+b-c - a) == (b-c) */ pa = _mm_sub_epi16(b, c); /* (p-b) == (a+b-c - b) == (a-c) */ pb = _mm_sub_epi16(a, c); /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ pc = _mm_add_epi16(pa, pb); pa = abs_i16(pa); /* |p-a| */ pb = abs_i16(pb); /* |p-b| */ pc = abs_i16(pc); /* |p-c| */ smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); /* Paeth breaks ties favoring a over b over c. */ nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c)); /* Note `_epi8`: we need addition to wrap modulo 255. */ d = _mm_add_epi8(d, nearest); store3(row, _mm_packus_epi16(d, d)); } } static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev) { /* Paeth tries to predict pixel d using the pixel to the left of it, a, * and two pixels from the previous row, b and c: * prev: c b * row: a d * The Paeth function predicts d to be whichever of a, b, or c is nearest to * p=a+b-c. * * The first pixel has no left context, and so uses an Up filter, p = b. * This works naturally with our main loop's p = a+b-c if we force a and c * to zero. * Here we zero b and d, which become c and a respectively at the start of * the loop. */ size_t rb = rowbytes+4; const __m128i zero = _mm_setzero_si128(); __m128i pa, pb, pc, smallest, nearest; __m128i c, b = zero, a, d = zero; while(rb > 4) { /* It's easiest to do this math (particularly, deal with pc) with 16-bit * intermediates. */ c = b; b = _mm_unpacklo_epi8(load4(prev), zero); a = d; d = _mm_unpacklo_epi8(load4(row ), zero); /* (p-a) == (a+b-c - a) == (b-c) */ pa = _mm_sub_epi16(b, c); /* (p-b) == (a+b-c - b) == (a-c) */ pb = _mm_sub_epi16(a, c); /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ pc = _mm_add_epi16(pa, pb); pa = abs_i16(pa); /* |p-a| */ pb = abs_i16(pb); /* |p-b| */ pc = abs_i16(pc); /* |p-c| */ smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); /* Paeth breaks ties favoring a over b over c. */ nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c)); /* Note `_epi8`: we need addition to wrap modulo 255. */ d = _mm_add_epi8(d, nearest); store4(row, _mm_packus_epi16(d, d)); prev += 4; row += 4; rb -= 4; } } #endif /* SPNG_X86 */ #if defined(SPNG_ARM) /* NEON optimised filter functions * Derived from filter_neon_intrinsics.c * * Copyright (c) 2018 Cosmin Truta * Copyright (c) 2014,2016 Glenn Randers-Pehrson * Written by James Yu , October 2013. * Based on filter_neon.S, written by Mans Rullgard, 2011. * * This code is derived from libpng source code. * For conditions of distribution and use, see the disclaimer * and license in this file. */ #define png_aligncast(type, value) ((void*)(value)) #define png_aligncastconst(type, value) ((const void*)(value)) /* libpng row pointers are not necessarily aligned to any particular boundary, * however this code will only work with appropriate alignment. mips/mips_init.c * checks for this (and will not compile unless it is done). This code uses * variants of png_aligncast to avoid compiler warnings. */ #define png_ptr(type,pointer) png_aligncast(type *,pointer) #define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer) /* The following relies on a variable 'temp_pointer' being declared with type * 'type'. This is written this way just to hide the GCC strict aliasing * warning; note that the code is safe because there never is an alias between * the input and output pointers. */ #define png_ldr(type,pointer)\ (temp_pointer = png_ptr(type,pointer), *temp_pointer) #if defined(_MSC_VER) && !defined(__clang__) && defined(_M_ARM64) #include #else #include #endif static void defilter_sub3(size_t rowbytes, unsigned char *row) { unsigned char *rp = row; unsigned char *rp_stop = row + rowbytes; uint8x16_t vtmp = vld1q_u8(rp); uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp); uint8x8x2_t vrp = *vrpt; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); for (; rp < rp_stop;) { uint8x8_t vtmp1, vtmp2; uint32x2_t *temp_pointer; vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6); vdest.val[1] = vadd_u8(vdest.val[0], vtmp1); vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); vdest.val[2] = vadd_u8(vdest.val[1], vtmp2); vdest.val[3] = vadd_u8(vdest.val[2], vtmp1); vtmp = vld1q_u8(rp + 12); vrpt = png_ptr(uint8x8x2_t, &vtmp); vrp = *vrpt; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } } static void defilter_sub4(size_t rowbytes, unsigned char *row) { unsigned char *rp = row; unsigned char *rp_stop = row + rowbytes; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); for (; rp < rp_stop; rp += 16) { uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp)); uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp); uint8x8x4_t vrp = *vrpt; uint32x2x4_t *temp_pointer; uint32x2x4_t vdest_val; vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]); vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]); vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]); vdest_val = png_ldr(uint32x2x4_t, &vdest); vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0); } } static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) { unsigned char *rp = row; const unsigned char *pp = prev_row; unsigned char *rp_stop = row + rowbytes; uint8x16_t vtmp; uint8x8x2_t *vrpt; uint8x8x2_t vrp; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); vtmp = vld1q_u8(rp); vrpt = png_ptr(uint8x8x2_t,&vtmp); vrp = *vrpt; for (; rp < rp_stop; pp += 12) { uint8x8_t vtmp1, vtmp2, vtmp3; uint8x8x2_t *vppt; uint8x8x2_t vpp; uint32x2_t *temp_pointer; vtmp = vld1q_u8(pp); vppt = png_ptr(uint8x8x2_t,&vtmp); vpp = *vppt; vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3); vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6); vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2); vdest.val[1] = vadd_u8(vdest.val[1], vtmp1); vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6); vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); vtmp = vld1q_u8(rp + 12); vrpt = png_ptr(uint8x8x2_t,&vtmp); vrp = *vrpt; vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2); vdest.val[2] = vadd_u8(vdest.val[2], vtmp3); vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1); vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2); vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } } static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) { unsigned char *rp = row; unsigned char *rp_stop = row + rowbytes; const unsigned char *pp = prev_row; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); for (; rp < rp_stop; rp += 16, pp += 16) { uint32x2x4_t vtmp; uint8x8x4_t *vrpt, *vppt; uint8x8x4_t vrp, vpp; uint32x2x4_t *temp_pointer; uint32x2x4_t vdest_val; vtmp = vld4_u32(png_ptr(uint32_t,rp)); vrpt = png_ptr(uint8x8x4_t,&vtmp); vrp = *vrpt; vtmp = vld4_u32(png_ptrc(uint32_t,pp)); vppt = png_ptr(uint8x8x4_t,&vtmp); vpp = *vppt; vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]); vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]); vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]); vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]); vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]); vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]); vdest_val = png_ldr(uint32x2x4_t, &vdest); vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0); } } static uint8x8_t paeth_arm(uint8x8_t a, uint8x8_t b, uint8x8_t c) { uint8x8_t d, e; uint16x8_t p1, pa, pb, pc; p1 = vaddl_u8(a, b); /* a + b */ pc = vaddl_u8(c, c); /* c * 2 */ pa = vabdl_u8(b, c); /* pa */ pb = vabdl_u8(a, c); /* pb */ pc = vabdq_u16(p1, pc); /* pc */ p1 = vcleq_u16(pa, pb); /* pa <= pb */ pa = vcleq_u16(pa, pc); /* pa <= pc */ pb = vcleq_u16(pb, pc); /* pb <= pc */ p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */ d = vmovn_u16(pb); e = vmovn_u16(p1); d = vbsl_u8(d, b, c); e = vbsl_u8(e, a, d); return e; } static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) { unsigned char *rp = row; const unsigned char *pp = prev_row; unsigned char *rp_stop = row + rowbytes; uint8x16_t vtmp; uint8x8x2_t *vrpt; uint8x8x2_t vrp; uint8x8_t vlast = vdup_n_u8(0); uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); vtmp = vld1q_u8(rp); vrpt = png_ptr(uint8x8x2_t,&vtmp); vrp = *vrpt; for (; rp < rp_stop; pp += 12) { uint8x8x2_t *vppt; uint8x8x2_t vpp; uint8x8_t vtmp1, vtmp2, vtmp3; uint32x2_t *temp_pointer; vtmp = vld1q_u8(pp); vppt = png_ptr(uint8x8x2_t,&vtmp); vpp = *vppt; vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast); vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3); vdest.val[1] = paeth_arm(vdest.val[0], vtmp2, vpp.val[0]); vdest.val[1] = vadd_u8(vdest.val[1], vtmp1); vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6); vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6); vdest.val[2] = paeth_arm(vdest.val[1], vtmp3, vtmp2); vdest.val[2] = vadd_u8(vdest.val[2], vtmp1); vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1); vtmp = vld1q_u8(rp + 12); vrpt = png_ptr(uint8x8x2_t,&vtmp); vrp = *vrpt; vdest.val[3] = paeth_arm(vdest.val[2], vtmp2, vtmp3); vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); vlast = vtmp2; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); rp += 3; vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } } static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) { unsigned char *rp = row; unsigned char *rp_stop = row + rowbytes; const unsigned char *pp = prev_row; uint8x8_t vlast = vdup_n_u8(0); uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); for (; rp < rp_stop; rp += 16, pp += 16) { uint32x2x4_t vtmp; uint8x8x4_t *vrpt, *vppt; uint8x8x4_t vrp, vpp; uint32x2x4_t *temp_pointer; uint32x2x4_t vdest_val; vtmp = vld4_u32(png_ptr(uint32_t,rp)); vrpt = png_ptr(uint8x8x4_t,&vtmp); vrp = *vrpt; vtmp = vld4_u32(png_ptrc(uint32_t,pp)); vppt = png_ptr(uint8x8x4_t,&vtmp); vpp = *vppt; vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast); vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); vdest.val[1] = paeth_arm(vdest.val[0], vpp.val[1], vpp.val[0]); vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]); vdest.val[2] = paeth_arm(vdest.val[1], vpp.val[2], vpp.val[1]); vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]); vdest.val[3] = paeth_arm(vdest.val[2], vpp.val[3], vpp.val[2]); vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]); vlast = vpp.val[3]; vdest_val = png_ldr(uint32x2x4_t, &vdest); vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0); } } /* NEON optimised palette expansion functions * Derived from palette_neon_intrinsics.c * * Copyright (c) 2018-2019 Cosmin Truta * Copyright (c) 2017-2018 Arm Holdings. All rights reserved. * Written by Richard Townsend , February 2017. * * This code is derived from libpng source code. * For conditions of distribution and use, see the disclaimer * and license in this file. * * Related: https://developer.arm.com/documentation/101964/latest/Color-palette-expansion * * The functions were refactored to iterate forward. * */ /* Expands a palettized row into RGBA8. */ static uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width) { const uint32_t scanline_stride = 4; const uint32_t row_stride = scanline_stride * 4; const uint32_t count = width / scanline_stride; const uint32_t *palette = (const uint32_t*)plte; if(!count) return 0; uint32_t i; uint32x4_t cur; for(i=0; i < count; i++, scanline += scanline_stride) { cur = vld1q_dup_u32 (palette + scanline[0]); cur = vld1q_lane_u32(palette + scanline[1], cur, 1); cur = vld1q_lane_u32(palette + scanline[2], cur, 2); cur = vld1q_lane_u32(palette + scanline[3], cur, 3); vst1q_u32((uint32_t*)(row + i * row_stride), cur); } return count * scanline_stride; } /* Expands a palettized row into RGB8. */ static uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width) { const uint32_t scanline_stride = 8; const uint32_t row_stride = scanline_stride * 3; const uint32_t count = width / scanline_stride; if(!count) return 0; uint32_t i; uint8x8x3_t cur; for(i=0; i < count; i++, scanline += scanline_stride) { cur = vld3_dup_u8 (plte + 3 * scanline[0]); cur = vld3_lane_u8(plte + 3 * scanline[1], cur, 1); cur = vld3_lane_u8(plte + 3 * scanline[2], cur, 2); cur = vld3_lane_u8(plte + 3 * scanline[3], cur, 3); cur = vld3_lane_u8(plte + 3 * scanline[4], cur, 4); cur = vld3_lane_u8(plte + 3 * scanline[5], cur, 5); cur = vld3_lane_u8(plte + 3 * scanline[6], cur, 6); cur = vld3_lane_u8(plte + 3 * scanline[7], cur, 7); vst3_u8(row + i * row_stride, cur); } return count * scanline_stride; } #endif /* SPNG_ARM */ libspng-0.7.4/spng/spng.h000066400000000000000000000320321442617636000152600ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ #ifndef SPNG_H #define SPNG_H #ifdef __cplusplus extern "C" { #endif #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(SPNG_STATIC) #if defined(SPNG__BUILD) #define SPNG_API __declspec(dllexport) #else #define SPNG_API __declspec(dllimport) #endif #else #define SPNG_API #endif #if defined(_MSC_VER) #define SPNG_CDECL __cdecl #else #define SPNG_CDECL #endif #include #include #include #define SPNG_VERSION_MAJOR 0 #define SPNG_VERSION_MINOR 7 #define SPNG_VERSION_PATCH 4 enum spng_errno { SPNG_IO_ERROR = -2, SPNG_IO_EOF = -1, SPNG_OK = 0, SPNG_EINVAL, SPNG_EMEM, SPNG_EOVERFLOW, SPNG_ESIGNATURE, SPNG_EWIDTH, SPNG_EHEIGHT, SPNG_EUSER_WIDTH, SPNG_EUSER_HEIGHT, SPNG_EBIT_DEPTH, SPNG_ECOLOR_TYPE, SPNG_ECOMPRESSION_METHOD, SPNG_EFILTER_METHOD, SPNG_EINTERLACE_METHOD, SPNG_EIHDR_SIZE, SPNG_ENOIHDR, SPNG_ECHUNK_POS, SPNG_ECHUNK_SIZE, SPNG_ECHUNK_CRC, SPNG_ECHUNK_TYPE, SPNG_ECHUNK_UNKNOWN_CRITICAL, SPNG_EDUP_PLTE, SPNG_EDUP_CHRM, SPNG_EDUP_GAMA, SPNG_EDUP_ICCP, SPNG_EDUP_SBIT, SPNG_EDUP_SRGB, SPNG_EDUP_BKGD, SPNG_EDUP_HIST, SPNG_EDUP_TRNS, SPNG_EDUP_PHYS, SPNG_EDUP_TIME, SPNG_EDUP_OFFS, SPNG_EDUP_EXIF, SPNG_ECHRM, SPNG_EPLTE_IDX, SPNG_ETRNS_COLOR_TYPE, SPNG_ETRNS_NO_PLTE, SPNG_EGAMA, SPNG_EICCP_NAME, SPNG_EICCP_COMPRESSION_METHOD, SPNG_ESBIT, SPNG_ESRGB, SPNG_ETEXT, SPNG_ETEXT_KEYWORD, SPNG_EZTXT, SPNG_EZTXT_COMPRESSION_METHOD, SPNG_EITXT, SPNG_EITXT_COMPRESSION_FLAG, SPNG_EITXT_COMPRESSION_METHOD, SPNG_EITXT_LANG_TAG, SPNG_EITXT_TRANSLATED_KEY, SPNG_EBKGD_NO_PLTE, SPNG_EBKGD_PLTE_IDX, SPNG_EHIST_NO_PLTE, SPNG_EPHYS, SPNG_ESPLT_NAME, SPNG_ESPLT_DUP_NAME, SPNG_ESPLT_DEPTH, SPNG_ETIME, SPNG_EOFFS, SPNG_EEXIF, SPNG_EIDAT_TOO_SHORT, SPNG_EIDAT_STREAM, SPNG_EZLIB, SPNG_EFILTER, SPNG_EBUFSIZ, SPNG_EIO, SPNG_EOF, SPNG_EBUF_SET, SPNG_EBADSTATE, SPNG_EFMT, SPNG_EFLAGS, SPNG_ECHUNKAVAIL, SPNG_ENCODE_ONLY, SPNG_EOI, SPNG_ENOPLTE, SPNG_ECHUNK_LIMITS, SPNG_EZLIB_INIT, SPNG_ECHUNK_STDLEN, SPNG_EINTERNAL, SPNG_ECTXTYPE, SPNG_ENOSRC, SPNG_ENODST, SPNG_EOPSTATE, SPNG_ENOTFINAL, }; enum spng_text_type { SPNG_TEXT = 1, SPNG_ZTXT = 2, SPNG_ITXT = 3 }; enum spng_color_type { SPNG_COLOR_TYPE_GRAYSCALE = 0, SPNG_COLOR_TYPE_TRUECOLOR = 2, SPNG_COLOR_TYPE_INDEXED = 3, SPNG_COLOR_TYPE_GRAYSCALE_ALPHA = 4, SPNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6 }; enum spng_filter { SPNG_FILTER_NONE = 0, SPNG_FILTER_SUB = 1, SPNG_FILTER_UP = 2, SPNG_FILTER_AVERAGE = 3, SPNG_FILTER_PAETH = 4 }; enum spng_filter_choice { SPNG_DISABLE_FILTERING = 0, SPNG_FILTER_CHOICE_NONE = 8, SPNG_FILTER_CHOICE_SUB = 16, SPNG_FILTER_CHOICE_UP = 32, SPNG_FILTER_CHOICE_AVG = 64, SPNG_FILTER_CHOICE_PAETH = 128, SPNG_FILTER_CHOICE_ALL = (8|16|32|64|128) }; enum spng_interlace_method { SPNG_INTERLACE_NONE = 0, SPNG_INTERLACE_ADAM7 = 1 }; /* Channels are always in byte-order */ enum spng_format { SPNG_FMT_RGBA8 = 1, SPNG_FMT_RGBA16 = 2, SPNG_FMT_RGB8 = 4, /* Partially implemented, see documentation */ SPNG_FMT_GA8 = 16, SPNG_FMT_GA16 = 32, SPNG_FMT_G8 = 64, /* No conversion or scaling */ SPNG_FMT_PNG = 256, SPNG_FMT_RAW = 512 /* big-endian (everything else is host-endian) */ }; enum spng_ctx_flags { SPNG_CTX_IGNORE_ADLER32 = 1, /* Ignore checksum in DEFLATE streams */ SPNG_CTX_ENCODER = 2 /* Create an encoder context */ }; enum spng_decode_flags { SPNG_DECODE_USE_TRNS = 1, /* Deprecated */ SPNG_DECODE_USE_GAMA = 2, /* Deprecated */ SPNG_DECODE_USE_SBIT = 8, /* Undocumented */ SPNG_DECODE_TRNS = 1, /* Apply transparency */ SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */ SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */ }; enum spng_crc_action { /* Default for critical chunks */ SPNG_CRC_ERROR = 0, /* Discard chunk, invalid for critical chunks. Since v0.6.2: default for ancillary chunks */ SPNG_CRC_DISCARD = 1, /* Ignore and don't calculate checksum. Since v0.6.2: also ignores checksums in DEFLATE streams */ SPNG_CRC_USE = 2 }; enum spng_encode_flags { SPNG_ENCODE_PROGRESSIVE = 1, /* Initialize for progressive writes */ SPNG_ENCODE_FINALIZE = 2, /* Finalize PNG after encoding image */ }; struct spng_ihdr { uint32_t width; uint32_t height; uint8_t bit_depth; uint8_t color_type; uint8_t compression_method; uint8_t filter_method; uint8_t interlace_method; }; struct spng_plte_entry { uint8_t red; uint8_t green; uint8_t blue; uint8_t alpha; /* Reserved for internal use */ }; struct spng_plte { uint32_t n_entries; struct spng_plte_entry entries[256]; }; struct spng_trns { uint16_t gray; uint16_t red; uint16_t green; uint16_t blue; uint32_t n_type3_entries; uint8_t type3_alpha[256]; }; struct spng_chrm_int { uint32_t white_point_x; uint32_t white_point_y; uint32_t red_x; uint32_t red_y; uint32_t green_x; uint32_t green_y; uint32_t blue_x; uint32_t blue_y; }; struct spng_chrm { double white_point_x; double white_point_y; double red_x; double red_y; double green_x; double green_y; double blue_x; double blue_y; }; struct spng_iccp { char profile_name[80]; size_t profile_len; char *profile; }; struct spng_sbit { uint8_t grayscale_bits; uint8_t red_bits; uint8_t green_bits; uint8_t blue_bits; uint8_t alpha_bits; }; struct spng_text { char keyword[80]; int type; size_t length; char *text; uint8_t compression_flag; /* iTXt only */ uint8_t compression_method; /* iTXt, ztXt only */ char *language_tag; /* iTXt only */ char *translated_keyword; /* iTXt only */ }; struct spng_bkgd { uint16_t gray; /* Only for gray/gray alpha */ uint16_t red; uint16_t green; uint16_t blue; uint16_t plte_index; /* Only for indexed color */ }; struct spng_hist { uint16_t frequency[256]; }; struct spng_phys { uint32_t ppu_x, ppu_y; uint8_t unit_specifier; }; struct spng_splt_entry { uint16_t red; uint16_t green; uint16_t blue; uint16_t alpha; uint16_t frequency; }; struct spng_splt { char name[80]; uint8_t sample_depth; uint32_t n_entries; struct spng_splt_entry *entries; }; struct spng_time { uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; }; struct spng_offs { int32_t x, y; uint8_t unit_specifier; }; struct spng_exif { size_t length; char *data; }; struct spng_chunk { size_t offset; uint32_t length; uint8_t type[4]; uint32_t crc; }; enum spng_location { SPNG_AFTER_IHDR = 1, SPNG_AFTER_PLTE = 2, SPNG_AFTER_IDAT = 8, }; struct spng_unknown_chunk { uint8_t type[4]; size_t length; void *data; enum spng_location location; }; enum spng_option { SPNG_KEEP_UNKNOWN_CHUNKS = 1, SPNG_IMG_COMPRESSION_LEVEL, SPNG_IMG_WINDOW_BITS, SPNG_IMG_MEM_LEVEL, SPNG_IMG_COMPRESSION_STRATEGY, SPNG_TEXT_COMPRESSION_LEVEL, SPNG_TEXT_WINDOW_BITS, SPNG_TEXT_MEM_LEVEL, SPNG_TEXT_COMPRESSION_STRATEGY, SPNG_FILTER_CHOICE, SPNG_CHUNK_COUNT_LIMIT, SPNG_ENCODE_TO_BUFFER, }; typedef void* SPNG_CDECL spng_malloc_fn(size_t size); typedef void* SPNG_CDECL spng_realloc_fn(void* ptr, size_t size); typedef void* SPNG_CDECL spng_calloc_fn(size_t count, size_t size); typedef void SPNG_CDECL spng_free_fn(void* ptr); struct spng_alloc { spng_malloc_fn *malloc_fn; spng_realloc_fn *realloc_fn; spng_calloc_fn *calloc_fn; spng_free_fn *free_fn; }; struct spng_row_info { uint32_t scanline_idx; uint32_t row_num; /* deinterlaced row index */ int pass; uint8_t filter; }; typedef struct spng_ctx spng_ctx; typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length); typedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length); typedef int spng_rw_fn(spng_ctx *ctx, void *user, void *dst_src, size_t length); SPNG_API spng_ctx *spng_ctx_new(int flags); SPNG_API spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags); SPNG_API void spng_ctx_free(spng_ctx *ctx); SPNG_API int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size); SPNG_API int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user); SPNG_API int spng_set_png_file(spng_ctx *ctx, FILE *file); SPNG_API void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error); SPNG_API int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height); SPNG_API int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height); SPNG_API int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_size); SPNG_API int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_size); SPNG_API int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary); SPNG_API int spng_set_option(spng_ctx *ctx, enum spng_option option, int value); SPNG_API int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value); SPNG_API int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len); /* Decode */ SPNG_API int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags); /* Progressive decode */ SPNG_API int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len); SPNG_API int spng_decode_row(spng_ctx *ctx, void *out, size_t len); SPNG_API int spng_decode_chunks(spng_ctx *ctx); /* Encode/decode */ SPNG_API int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info); /* Encode */ SPNG_API int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags); /* Progressive encode */ SPNG_API int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len); SPNG_API int spng_encode_row(spng_ctx *ctx, const void *row, size_t len); SPNG_API int spng_encode_chunks(spng_ctx *ctx); SPNG_API int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr); SPNG_API int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte); SPNG_API int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns); SPNG_API int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm); SPNG_API int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int); SPNG_API int spng_get_gama(spng_ctx *ctx, double *gamma); SPNG_API int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int); SPNG_API int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp); SPNG_API int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit); SPNG_API int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent); SPNG_API int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text); SPNG_API int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd); SPNG_API int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist); SPNG_API int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys); SPNG_API int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt); SPNG_API int spng_get_time(spng_ctx *ctx, struct spng_time *time); SPNG_API int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks); /* Official extensions */ SPNG_API int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs); SPNG_API int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif); SPNG_API int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr); SPNG_API int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte); SPNG_API int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns); SPNG_API int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm); SPNG_API int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int); SPNG_API int spng_set_gama(spng_ctx *ctx, double gamma); SPNG_API int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma); SPNG_API int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp); SPNG_API int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit); SPNG_API int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent); SPNG_API int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text); SPNG_API int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd); SPNG_API int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist); SPNG_API int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys); SPNG_API int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt); SPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time); SPNG_API int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks); /* Official extensions */ SPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs); SPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif); SPNG_API const char *spng_strerror(int err); SPNG_API const char *spng_version_string(void); #ifdef __cplusplus } #endif #endif /* SPNG_H */ libspng-0.7.4/subprojects/000077500000000000000000000000001442617636000155345ustar00rootroot00000000000000libspng-0.7.4/subprojects/benchmark_images.wrap000066400000000000000000000001541442617636000217060ustar00rootroot00000000000000[wrap-git] directory = benchmark_images url = https://github.com/libspng/benchmark_images.git revision=head libspng-0.7.4/subprojects/fuzzing_corpora.wrap000066400000000000000000000001511442617636000216450ustar00rootroot00000000000000[wrap-git] directory = fuzzing_corpora url = https://github.com/libspng/fuzzing_corpora.git revision=headlibspng-0.7.4/subprojects/libpng.wrap000066400000000000000000000006521442617636000177050ustar00rootroot00000000000000[wrap-file] directory = libpng-1.6.37 source_url = https://github.com/glennrp/libpng/archive/v1.6.37.tar.gz source_filename = libpng-1.6.37.tar.gz source_hash = ca74a0dace179a8422187671aee97dd3892b53e168627145271cad5b5ac81307 patch_url = https://wrapdb.mesonbuild.com/v1/projects/libpng/1.6.37/1/get_zip patch_filename = libpng-1.6.37-1-wrap.zip patch_hash = 9a863ae8a5657315a484c94c51f9f636b1fb9f49a15196cc896b72e5f21d78f0 libspng-0.7.4/subprojects/miniz.wrap000066400000000000000000000001371442617636000175560ustar00rootroot00000000000000[wrap-git] directory = miniz-2.2.0 url = https://github.com/richgel999/miniz.git revision=2.2.0libspng-0.7.4/subprojects/spngt.wrap000066400000000000000000000001251442617636000175600ustar00rootroot00000000000000[wrap-git] directory = spngt url = https://github.com/libspng/spngt.git revision=headlibspng-0.7.4/subprojects/zlib.wrap000066400000000000000000000006501442617636000173700ustar00rootroot00000000000000[wrap-file] directory = zlib-1.2.13 source_url = http://zlib.net/fossils/zlib-1.2.13.tar.gz source_filename = zlib-1.2.13.tar.gz source_hash = b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30 patch_filename = zlib_1.2.13-1_patch.zip patch_url = https://wrapdb.mesonbuild.com/v2/zlib_1.2.13-1/get_patch patch_hash = 73a0103df54133b10f8774f92e23da048bd22554523e2b833cdb72b2702c0628 [provide] zlib = zlib_dep libspng-0.7.4/tests/000077500000000000000000000000001442617636000143335ustar00rootroot00000000000000libspng-0.7.4/tests/README.md000066400000000000000000000042551442617636000156200ustar00rootroot00000000000000# Testing Unit testing requires Meson and libpng, currently only developer builds expose the test cases, this is enabled with `meson configure -Ddev_build=true`. All unit tests are run with the `meson test` command, to enable sanitizers: ```bash meson configure -Db_sanitize=address,undefined ``` Testing with MemorySanitizer requires all dependencies to be instrumented, Meson can be forced to download libpng and zlib and build them from source using the `--wrap-mode=forcefallback` option when creating a build. ## Correctness To ensure correctness the decoder is tested with all supported output formats and decode flags against every PNG color type and bit depth combination. The testsuite uses Meson's unit testing framework, images from the [PngSuite](http://web.archive.org/web/20200414214727/www.schaik.com/pngsuite/) and libpng to verify the results. For each PNG test cases are created with unique libspng output format and decode flag combinations, a translation layer converts these to libpng calls and the image is decoded with both libraries. To pass each test the output images have to be bit-identical, for gamma-corrected images each color / grayscale sample has to be within 2%. The testsuite also covers deinterlacing with 1, 2, 4-bit samples. ## Regression tests The `crashers` directory contains regressions tests, some of these files were copied from the libpng repository. ## Fuzz testing Code is continuously fuzzed on [OSS-Fuzz](https://google.github.io/oss-fuzz/) using [`spng_read_fuzzer.c`](spng_read_fuzzer.c) as the fuzz target, [code coverage](https://oss-fuzz.com/coverage-report/job/libfuzzer_asan_libspng/latest) information is constantly updated. Pull requests are also tested with [CIFuzz](https://google.github.io/oss-fuzz/getting-started/continuous-integration/), this runs a short fuzz test and catches most bugs before they could be merged. The `fuzz_repro` executable is used for reproducing test cases, it uses a dummy entrypoint to replace the libFuzzer dependency. ## Fuzzing corpora Regression tests can be run against the fuzzing corpora created by OSS-Fuzz, this is enabled with the `oss_fuzz` option, the tests are run with the same `meson test` command.libspng-0.7.4/tests/crashers/000077500000000000000000000000001442617636000161455ustar00rootroot00000000000000libspng-0.7.4/tests/crashers/bad_iCCP.png000066400000000000000000000005011442617636000202330ustar00rootroot00000000000000‰PNG  IHDR ́gÅiCCP)c#Ķ+``ŧÆL Å1’EîAŽ‘‘Q ė;2x€P„)1š¸€Ŋũ]Ũƒ\#PŽk Œ ꚎW€Ģ:i@'9 ™*':%3oĩHsɍ‰sRGžŗNG IipRCAD'ö‰GBNR~sPw oĩXDATlibspng-0.7.4/tests/crashers/badadler.png000066400000000000000000000001031442617636000204030ustar00rootroot00000000000000‰PNG  IHDR7nų$ IDAT×c`Ģ­­2›ØIENDŽB`‚libspng-0.7.4/tests/crashers/badcrc.png000066400000000000000000000001031442617636000200630ustar00rootroot00000000000000‰PNG  IHDR7nų$ IDAT×c`Ģ­Ė2IENDŽB`‚libspng-0.7.4/tests/crashers/empty_ancillary_chunks.png000066400000000000000000000013321442617636000234210ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘbKGD-ŪūcHRMöžYreXIfĘy—gAMA˛áˇhIST] -ÅiCCPĨ‰>iTXt)Ճ€juNk“¸›juNK¨˜SpCALޝ?æpHYs–‡ecsCALŧsPLT¸k:sRGBĶÎsTER;ŨštEXt–BŅtIMEų3ÚĪzTXtky{ņPLTE€€Ģ÷ĨzbKGD-ŪūcHRMöžYreXIfĘy—gAMA˛áˇhIST] -ÅiCCPĨ‰>iTXt)Ճ€juNk“¸›juNK¨˜SpCALޝ?æpHYs–‡ecsCALŧsPLT¸k:sRGBĶÎsTER;ŨštEXt–BŅtIMEų3ÚĪzTXtky{ņ IDAT×c`â!ŧ3bKGD-ŪūcHRMöžYreXIfĘy—gAMA˛áˇhIST] -ÅiCCPĨ‰>iTXt)Ճ€juNk“¸›juNK¨˜SpCALޝ?æpHYs–‡ecsCALŧsPLT¸k:sRGBĶÎsTER;ŨštEXt–BŅtIMEų3ÚĪzTXtky{ņIENDŽB`‚libspng-0.7.4/tests/crashers/huge_IDAT.png000066400000000000000000000001171442617636000204030ustar00rootroot00000000000000‰PNG  IHDRũԚs?˙˙˙IDAT×cüā4ÖŽî~+IENDŽB`‚libspng-0.7.4/tests/crashers/huge_bKGD_chunk.png000066400000000000000000000000711442617636000216200ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙bKGD-ŪūIENDŽB`‚libspng-0.7.4/tests/crashers/huge_cHRM_chunk.png000066400000000000000000000000711442617636000216420ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙cHRMöžYrIENDŽB`‚libspng-0.7.4/tests/crashers/huge_eXIf_chunk.png000066400000000000000000000000711442617636000217040ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙eXIfĘy—IENDŽB`‚libspng-0.7.4/tests/crashers/huge_gAMA_chunk.png000066400000000000000000000000711442617636000216160ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙gAMA˛áˇIENDŽB`‚libspng-0.7.4/tests/crashers/huge_hIST_chunk.png000066400000000000000000000000711442617636000216600ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙hIST] -ÅIENDŽB`‚libspng-0.7.4/tests/crashers/huge_iCCP_chunk.png000066400000000000000000000000711442617636000216270ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙iCCPĨ‰>IENDŽB`‚libspng-0.7.4/tests/crashers/huge_iTXt_chunk.png000066400000000000000000000000711442617636000217410ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙iTXt)Ճ€IENDŽB`‚libspng-0.7.4/tests/crashers/huge_juNK_unsafe_to_copy.png000066400000000000000000000000711442617636000236250ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙juNK¨˜SIENDŽB`‚libspng-0.7.4/tests/crashers/huge_juNk_safe_to_copy.png000066400000000000000000000000711442617636000233220ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙juNk“¸›IENDŽB`‚libspng-0.7.4/tests/crashers/huge_pCAL_chunk.png000066400000000000000000000000711442617636000216300ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙pCALޝ?æIENDŽB`‚libspng-0.7.4/tests/crashers/huge_pHYs_chunk.png000066400000000000000000000000711442617636000217340ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙pHYs–‡ecIENDŽB`‚libspng-0.7.4/tests/crashers/huge_sCAL_chunk.png000066400000000000000000000000711442617636000216330ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙sCALŧIENDŽB`‚libspng-0.7.4/tests/crashers/huge_sPLT_chunk.png000066400000000000000000000000711442617636000216730ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙sPLT¸k:IENDŽB`‚libspng-0.7.4/tests/crashers/huge_sRGB_chunk.png000066400000000000000000000000711442617636000216460ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙sRGBĶÎIENDŽB`‚libspng-0.7.4/tests/crashers/huge_sTER_chunk.png000066400000000000000000000000711442617636000216660ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙sTER;ŨšIENDŽB`‚libspng-0.7.4/tests/crashers/huge_tEXt_chunk.png000066400000000000000000000000711442617636000217350ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙tEXt–BŅIENDŽB`‚libspng-0.7.4/tests/crashers/huge_tIME_chunk.png000066400000000000000000000000711442617636000216470ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙tIMEų3ÚĪIENDŽB`‚libspng-0.7.4/tests/crashers/huge_zTXt_chunk.png000066400000000000000000000000711442617636000217620ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘ?˙˙˙zTXtky{ņIENDŽB`‚libspng-0.7.4/tests/crashers/invalid_gray_alpha_sbit.png000066400000000000000000000000761442617636000235140ustar00rootroot00000000000000‰PNG  IHDR ūä^ĒsBITIDATxœc ųlibspng-0.7.4/tests/crashers/meson.build000066400000000000000000000045401442617636000203120ustar00rootroot00000000000000test('badadler', test_exe, args : files('badadler.png'), should_fail : true) test('badcrc', test_exe, args : files('badcrc.png'), should_fail : true) test('bad_iccp', test_exe, args : files('bad_iCCP.png'), should_fail : true) test('empty_ancillary', test_exe, args : files('empty_ancillary_chunks.png'), should_fail : true) test('huge_bkgd', test_exe, args : files('huge_bKGD_chunk.png'), should_fail : true) test('huge_chrm', test_exe, args : files('huge_cHRM_chunk.png'), should_fail : true) test('huge_exif', test_exe, args : files('huge_eXIf_chunk.png'), should_fail : true) test('huge_gama', test_exe, args : files('huge_gAMA_chunk.png'), should_fail : true) test('huge_hist', test_exe, args : files('huge_hIST_chunk.png'), should_fail : true) test('huge_iccp', test_exe, args : files('huge_iCCP_chunk.png'), should_fail : true) test('huge_idat', test_exe, args : files('huge_IDAT.png'), should_fail : true) test('huge_itxt', test_exe, args : files('huge_iTXt_chunk.png'), should_fail : true) test('huge_junk_safe_to_copy', test_exe, args : files('huge_juNk_safe_to_copy.png'), should_fail : true) test('huge_junk_unsafe_to_copy', test_exe, args : files('huge_juNK_unsafe_to_copy.png'), should_fail : true) test('huge_pcal', test_exe, args : files('huge_pCAL_chunk.png'), should_fail : true) test('huge_phys', test_exe, args : files('huge_pHYs_chunk.png'), should_fail : true) test('huge_scal', test_exe, args : files('huge_sCAL_chunk.png'), should_fail : true) test('huge_splt', test_exe, args : files('huge_sPLT_chunk.png'), should_fail : true) test('huge_srgb', test_exe, args : files('huge_sRGB_chunk.png'), should_fail : true) test('huge_ster', test_exe, args : files('huge_sTER_chunk.png'), should_fail : true) test('huge_text', test_exe, args : files('huge_tEXt_chunk.png'), should_fail : true) test('huge_time', test_exe, args : files('huge_tIME_chunk.png'), should_fail : true) test('huge_ztxt', test_exe, args : files('huge_zTXt_chunk.png'), should_fail : true) # These are minimized testcases created by OSS-Fuzz with fixed CRC's test('zero_width', test_exe, args : files('zero_width.png'), should_fail : true) test('zero_gama', test_exe, args : files('zero_gama.png'), should_fail : true) test('invalid_gray_alpha_sbit', test_exe, args : files('invalid_gray_alpha_sbit.png'), should_fail : true) test('missing_plte', test_exe, args : files('missing_plte.png'), should_fail : true) libspng-0.7.4/tests/crashers/missing_plte.png000066400000000000000000000001601442617636000213450ustar00rootroot00000000000000‰PNG  IHDR I´čˇgAMA† 1č–_ppppî˙""f˙l%iIDATxœcā4â ¤ģ?Á“‚fYIENDŽB`‚libspng-0.7.4/tests/crashers/zero_gama.png000066400000000000000000000000711442617636000206150ustar00rootroot00000000000000‰PNG  IHDR  GEhYgAMA‹%`M IDATlibspng-0.7.4/tests/crashers/zero_width.png000066400000000000000000000000511442617636000210250ustar00rootroot00000000000000‰PNG  IHDR >ÛĘ- IDATlibspng-0.7.4/tests/framac_stubs.h000066400000000000000000000032711442617636000171600ustar00rootroot00000000000000#ifndef SPNG_FRAMAC_STUBS_H #define SPNG_FRAMAC_STUBS_H #include #define ZLIB_VERNUM 0x1290 #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 #define Z_TREES 6 #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) #define Z_NULL 0 typedef void* (*alloc_func)(void *opaque, unsigned int items, unsigned int size); typedef void (*free_func)(void *opaque, void *address); typedef struct z_stream_s { const unsigned char *next_in; unsigned int avail_in; unsigned long total_in; unsigned char *next_out; unsigned int avail_out; unsigned long total_out; alloc_func zalloc; free_func zfree; void *opaque; int data_type; unsigned long adler; }z_stream; typedef z_stream *z_streamp; int inflateInit(z_streamp strm) { if(strm == NULL) return 1; return 0; } int inflate(z_streamp stream, int flush) { if(stream==NULL) return Z_STREAM_ERROR; if(stream->avail_in == 0 || stream->avail_out == 0) return Z_BUF_ERROR; stream->next_in += stream->avail_in; stream->next_out += stream->avail_out; return 0; } int inflateEnd(z_streamp stream) { if(stream==NULL) return 1; return 0; } int inflateValidate(z_streamp stream, int b) { return 0; } unsigned long crc32(unsigned long crc, const unsigned char *buf, unsigned int len) { if(buf==NULL) return 0; return crc+len; } #endif /* SPNG_FRAMAC_STUBS_H */ libspng-0.7.4/tests/fuzz_main.c000066400000000000000000000016621442617636000165060ustar00rootroot00000000000000#include #include #include /* fuzz target entry point, works without libFuzzer */ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); int main(int argc, char **argv) { FILE *f; char *buf = NULL; long siz_buf; if(argc < 2) { fprintf(stderr, "no input file\n"); goto err; } f = fopen(argv[1], "rb"); if(f == NULL) { fprintf(stderr, "error opening input file %s\n", argv[1]); goto err; } fseek(f, 0, SEEK_END); siz_buf = ftell(f); rewind(f); if(siz_buf < 1) goto err; buf = (char*)malloc(siz_buf); if(buf == NULL) { fprintf(stderr, "malloc() failed\n"); goto err; } if(fread(buf, siz_buf, 1, f) != 1) { fprintf(stderr, "fread() failed\n"); goto err; } (void)LLVMFuzzerTestOneInput((uint8_t*)buf, siz_buf); err: free(buf); return 0; } libspng-0.7.4/tests/images/000077500000000000000000000000001442617636000156005ustar00rootroot00000000000000libspng-0.7.4/tests/images/PngSuite.LICENSE000066400000000000000000000002471442617636000203450ustar00rootroot00000000000000PngSuite -------- Permission to use, copy, modify and distribute these images for any purpose and without fee is hereby granted. (c) Willem van Schaik, 1996, 2011 libspng-0.7.4/tests/images/PngSuite.README000066400000000000000000000013141442617636000202140ustar00rootroot00000000000000 PNGSUITE ---------------- testset for PNG-(de)coders created by Willem van Schaik ------------------------------------ This is a collection of graphics images created to test the png applications like viewers, converters and editors. All (as far as that is possible) formats supported by the PNG standard are represented. The suite consists of the following files: - PngSuite.README - this file - PngSuite.LICENSE - the PngSuite is freeware - PngSuite.png - image with PngSuite logo - PngSuite.tgz - archive of all PNG testfiles - PngSuite.zip - same in .zip format for PCs -------- (c) Willem van Schaik willem@schaik.com Calgary, April 2011 libspng-0.7.4/tests/images/basi0g01.png000066400000000000000000000003311442617636000176110ustar00rootroot00000000000000‰PNG  IHDR ,wĪgAMA† 1č–_IDATxœ-1Â0 E߯‚Äz¤.z¸ƒą'ā V9cĨ€ØX,eÉ5|Kx°ŋŋßOpŒįŦšpŖōŅŪŲi\åŋYcßŲ*¯LŸŲ'ęDžd‡ß[œ6į™šß+MKÔOäKĢzŽæ”Ō2Š-cƒzpEHp-šŸôĄ/zQĒ!åĄŒũ”ãQf"ōIENDŽB`‚libspng-0.7.4/tests/images/basi0g02.png000066400000000000000000000002321442617636000176120ustar00rootroot00000000000000‰PNG  IHDR kĻ gAMA† 1č–_QIDATxœcPbč`ár†ģ`œ“Ãpė””–16nDgŗŽg]Īõ!ęÁĘdDáCH dJHRĮēę?à 0AĻ Á ’9œR€M…ļŲ}IENDŽB`‚libspng-0.7.4/tests/images/basi0g04.png000066400000000000000000000003671442617636000176250ustar00rootroot00000000000000‰PNG  IHDR äæøŋgAMA† 1č–_ŽIDATxœeŽQÂ0D„:(uę ÔAĀAÁAÁAĀA‘$T™I˜ÎÍ~Üėíģ›ÛÃ˛ÂļEuEûÄēÁžCsG÷Æjw<#Ö ą^ˆõÅbs8ĨAl.i›GZ‰Í'íĄ(•ŽũĢCÖYdõČ:"k@ÖižÃų2Gp‡r:ŋ1˙ƒĸ(KĨ´ŽkcŦe ­s ¤í{ŌiŊg í826ŌN'ëŠĶMIENDŽB`‚libspng-0.7.4/tests/images/basi0g08.png000066400000000000000000000003761442617636000176310ustar00rootroot00000000000000‰PNG  IHDR !žgAMA† 1č–_ĩIDATxœĩ] Â0„ēČBÄ{øCĪ%>ˆx°Ē!KÜĻž$]!Ĩ”ÎĶä›Í2,$UI˜ĒBH"¨*V$ĄĒŠ‹$ŏ™™™ ‰ J×l, įlōܲ 9:eSW}Ąq@ĸŞpėŋŖˆßņđ;‚û‚Ŋđ_|ą?€ģ­/Ö &Æ \íĪlXÅ5âRƒž“€§  ā͞Ü2ą‡'+d~G ˙@4ƒ @4Ã߁@Íđ\†åĖ\yÆ9ũÛŠÜĩEkQטĩ-Ž}Ļ §Q ÖFA Q ļwbk@ņõ‰‚|đūDÁđēāV˜î‰˙˙ūIENDŽB`‚libspng-0.7.4/tests/images/basi2c08.png000066400000000000000000000004731442617636000176250ustar00rootroot00000000000000‰PNG  IHDR ‹Ũ5gAMA† 1č–_ōIDATxœÕ“AĒ! DĢĀ{x=YĶs3Ŋ‰×hhĩfŅ ôgZøYd1Ąb|Vˆ‘’€ã%}Ö ¤ĪŽãĨķÆœ7æüÃô~gv ‹Úõ´îŌĶ>^/ôū-JcáĄm‘ sm‹ÄXTÅm•cô§āļmĪ Šķ˙ĸ”@‘å<¨lŠd9f+PJ™–`ÎŲ+š¨žĨdĻíႱ";@%ã,MãCcEv€É:­Ļɛs†ZĢ/Ā”’  ?]ßᗠ(Egƒ&Īû¯īüĶTôm›äy?‚s‡ü [Ä֚<#ÔZۀ1FWƒ7ßM 1!ü=3IENDŽB`‚libspng-0.7.4/tests/images/basi2c16.png000066400000000000000000000011231442617636000176150ustar00rootroot00000000000000‰PNG  IHDR ۏvgAMA† 1č–_ IDATxœÕ–!sã0…?Ī8ĖaĄ…Ĩ>VxĄ?Ą0….;Xx´°°0šp0 Sæ°W ¨‘+ģ‘cšņ ėėŧYŊ}Ī+EI$ ÖoëØ<ĩI}eEjëŽ@‘;;,PL“mÕŠĮâÆëą¸ņzXčĐCÚVđoŸbå!{kŖĄˇolž™čij*râÄûME.’ė–R¸Hmļf›ā ŗ‘°Á–7TaĨ_vh*…Ãg­oh/õ:œ*51‘vKxģØŨÃÉ|Pķ‘gEĮš…,sÂō`ZFjP'Č3€iČščNúšAë9:—ÔÉũcƒ Ÿ44čGęÖ8 â‘Öōj%Áëĩ_$Ø\Iđō,ÁĶB‚Į' Ęšwŋ%¸IđķQ‚"—āę—ķL‚ŧ”$Híåfåejī]|j8\´ØjSŒë NmōHš¨MŪÛA;ĩÉ{88EmōÔūÂĮ§˜>ttЅúč ¨Awjƒ88—:ĀA?ę/Ä nqÚs›Úä‰T­`ŋ†zŦ<äL|‰§Ίß›!–yŲmė=‹ģęĸ&&ߌxíĸ˛Ōā#›@˜hÁē‹vņ M Ÿh˙Æ Äíâgũē´h€É€ėĪ^´‹Gēß+Ú­ī1Ë‰vņއh˜ĀøDģxËÆ-Úŝ ü?ĸ]ü¤ŪŗßD÷IENDŽB`‚libspng-0.7.4/tests/images/basi3p01.png000066400000000000000000000002041442617636000176240ustar00rootroot00000000000000‰PNG  IHDR >ŗØ!gAMA† 1č–_PLTEî˙""f˙lŌ&)IDATxœc`€‚P¸ CҰą1Ù3ØIƒH6?@ˆ@!( ēB-vŠÃžIENDŽB`‚libspng-0.7.4/tests/images/basi3p02.png000066400000000000000000000003011442617636000176230ustar00rootroot00000000000000‰PNG  IHDR yĸņgAMA† 1č–_sBIT|.w‚ PLTE˙˙˙˙˙e?+ēQIDATxœcxĮ°›„]ÁØĖŒ!9JŪ¸bôöĸŗŋ.øē€?Š?ŠČøĘdđãCH dJHR÷?t#˜BĢ ,*ņÁ ’9´Z}éuáIENDŽB`‚libspng-0.7.4/tests/images/basi3p04.png000066400000000000000000000005071442617636000176350ustar00rootroot00000000000000‰PNG  IHDR öSWQgAMA† 1č–_sBITwøĩŖ-PLTE"˙˙˙ˆ˙"˙™˙˙fŨ˙w˙˙˙™Ũ˙˙ģ˙ģD˙˙DŌ°IŊļIDATxœcčŠc8ūĄN–áá%†(K†ÅS,ϰ1Ä,ˇ›ČÄž @ĖÄÄŲ ­§Š_B$€ ˆ,qĸČPƒ¨ë=ŗĒÜøŨL(-ČĨ] ô]ÍĨ• t”Ū0Ķ@L“1­Ā´ ĶRLÛ1pOGhč™3ĢV•—ŋ{7s&Ļ*WPS•ëâ‚)€ĘŊ{SË€)€ĘURÂ@åĻĨa  rwvęh tĐ_IENDŽB`‚libspng-0.7.4/tests/images/basi3p08.png000066400000000000000000000027671442617636000176530ustar00rootroot00000000000000‰PNG  IHDR 3ŖēPgAMA† 1č–_PLTE"Dõ˙íw˙wË˙˙ :w""˙˙˙""˙ŦUf˙f˙ff˙˙"Ü˙˙Ė˙™DD˙UU"ËË˙DDU˙UËË3˙ėÜí˙˙ä˙Ë˙ÜÜD˙Dff˙3D"íí˙ff˙¤D˙˙ĒííËËū˙˙ũ˙ū˙˙3˙3U*˙ˆˆ˙ĒĒDˆˆ˙äËē["˙"f2˙˙™ĒĒ˙UĒĒËc˙Ô˙Ēw:˙DDÜkf˙ˆBė˙ÜkÜ˙Üē33íís˙˙ˆ™J˙˙w˙ƒ˙ēēū{˙ū˙Ë˙™™"˙˙ˆ˙˙wˆˆ˙Ü˙3Ē3˙˙™™2f˙ē˙D˙˙˙Ē˙wūūĒJ™˙˙f˙""™‹˙U˙˙˙˙ˆ˙U˙˙ū˙ũū¤˙Df˙˙˙f˙3˙˙U˙wwˆ˙D˙w˙˙ff˙˙í˙õí˙˙˙D˙"˙˙ííˆ˙˙w“˙"ÜÜ33˙ūūēē˙™˙˙33cËēēŦ˙U˙˙Ü˙˙3{ūííUU˙Ē˙˙ÜÜ˙UUfÜÜÜ܃˙ww˙ūū˙˙˙Ë˙UUwwūūËËū"˙˙"DD›˙3˙ÔĒU™™˙™™ēē*U˙ËË´˙f˙›3˙˙ēĒĒBˆSĒ˙ĒĒíēē˙˙ūD™™™™˙Ė™ēˆˆÜ˙“"Üū˙ūĒSwwË3˙í˙ē˙33í˙í˙Ĉŧ˙wĒff""Ü˙Ë˙Ü˙Ü˙‹ËUUˆ"˙˙Ë˙Ëí˙ˆˆDD[ē˙ŧw˙™˙fē˙ēwwsíū33ē˙w˙DĒ˙Ē˙ūū""Ä˙ˆíí™˙™˙U˙"˙´f ˙Ü˙ēē˙˙ˆ˙ˆ˙3˙šŽĶbĸIDATxœeĐi\ đ7АJ”ŌMŦc))Iå¨ŌDXŠĘQaŅĄ29s%"BÖ!WŅ*ä˜ČM%Ę:9ŠåZ<ßú°˙į÷}ĪķR†E4›XL1‡‚MÔė.5#ž°Ž—\øŪōHmP!‘„B–zԞ]ÜBeöš‰ž&ĩéähĒã§AšViڍōę5”ÁŗF׹{û†Y.LžØ›#čÛ ö0Mun`¨ô­7‹Ü%RŽYHß5ĩ!C33ģģĢĢëë;;ÉÉÉÆÆĀĀסĩ522%ĨNJJKíė *+ÍÍ}|ōķŠŠÉÍÍØ¸­ÍÕĩĢ̍()‰‚‚JJ˛˛Ŧ­ŊŧââHYš§'00/OI),LW7<œ srŒŒ::’“mmũũ)>>&&*JNŽĸBKK_ŋŧœ22x< Ą0:ēŽŽÍîí• fiЧ§ĄAÔŪž]\ÜŌ"›”Årqa2ų|ąØÛ›ÃdŖ—•ŲÛkj&&zzš˜ÔÖϧËv öđ05MMuvnh`0TTdË9:ÆÆ††ęčøųŠĒFDČļmn‰ÜŨ%Š”Ë53 ‘­Ÿ›ke•–Ļ­ŨØ(/¯Ž^S#ûAĐvÂ/Øāü#á,Í0 žĀ' É0NĀ5XÃáŧš Sá €•đvÁc å0ŽĀjXw` Üēá ŧ§p>ÃU 0 N xģá!œúķá<…Û° .Ā^ ßđ nÁZ˜s` z×á\„ņ°öĀ? ŗ0ļĀ!ëāüÚ oa<‡~°Nà…p&Á0øûá2ŧš3áü÷đöÁ  Ņ0 Ãtx Cá8,ú ›`#ŒƒG0ÎĀøsūūr¯ÁåIENDŽB`‚libspng-0.7.4/tests/images/basi4a08.png000066400000000000000000000003261442617636000176220ustar00rootroot00000000000000‰PNG  IHDR Žt‚égAMA† 1č–_IDATxœíÍ ƒ0 ĮôÖŊAû(öÁ÷(sO°íäĩĸAV†˜Ō˙G":ÉŨ™bŲH$ ķ€@œŗü Ą›Éžđ‘čzMŠ2x<7UĨ[0tÔ<n!šYģ~.÷ļ,>āR¨fqXAhžŲĒwΤ5Ŧ0o0N°Á N¸6ø‡ Ž ŒėO= YU]IENDŽB`‚libspng-0.7.4/tests/images/basi4a16.png000066400000000000000000000054471442617636000176320ustar00rootroot00000000000000‰PNG  IHDR ūä^ĒgAMA† 1č–_ ŪIDATxœ—pT×uĮ?Z=IowŅęíŽB2Oˆß„ „5ĐÔf ãpҚfĻ4Žbôœé8OÔ5vH\âŠņ ÆC°c7–ãÔŽ#-+06P‚ Ђ˛´zW+íę­tĩęOL2m<“?žsÎÜ?î|Īšį|ĪšŲæLøÍë`×@–9ŒĻ>‡%K,ƒ‘ˆ›:Œ´ ë쇞íøö–ĒįoیÍŲ! ~>6€K÷2m4ä?ą)¤uü7,㍝ĘË€Īö‡4uže–žqCYßÍcgg'âĘČ i!˙lņBč4MÂŅ\xË2s&ô߆OŸiëŌŽî‡Cģ-ãŅhDđëīÃŊŲgëŋtĄ<ĐKz åēzę)]}į<”sļ~éÅû@šŋ ¤ ցBWTa3ŸFäÍe”ˇįĀĩ ;FŲŲúŸa_ß ĩ{ Ŧ”ˇįBKŦ:tļ~ŪWžN8ķ 4ũ Üŋ˛)ŨŊpfnėƒ}Nrîæaė‘˜ĨĢŗu÷ė~˜˙˙Ļ =8‰Â?ņƒ0ŅlJHƒžßÁ͗tõ7 ēÚ{ FŗÁ3%CP`CŋĮ‰ããø¨2[ÆÂ‚˜]úŠe+! í0ā…îáĘ6]ũYĢŽ&Š`ú4đ?įî8IH˙Â2VÄėō[vQ=4Ō@ļÃX>¤?‚ä30°.—ęęWßÕÕ×\Z†QŗKžiŲžnp7Bn(‡Aą í;Ãë` n~"OÎĮ,Ŗĸž Ū:XÔĶ@Áapŋy(b.ôŊ7ŋ ž9Ģ-#ė˛Xŋ1s;úWhÎĀūŨPŊfīéS!¸˛6€î°îîØĻĢŽrÍŦŠÂ’(T€Ē(o‡ķ+áܟā| ĖKÆė˛WcļÕfŗŪļ¨Ü€˜ĩ fCņ>đ„AŲ Úy(k†ąfPŋEMpũx¯˛4tõķ! ˆŲŸ%k–Öx3ė7ķâ0%ū8$ûÄNÄAū-Œ!„xz‚Đ„X!čjlÄ5+ .S3;ļéę-[Wû~ĸ̃ķuÕ>ĄĢ™Gtu,_W“ßĶՁëēÚSĢĢ7_×ÕĢ…ē:ģßa¨Tn÷¸ZŽ™—øMßŗ0íļķ¤™cöõt—Âĩv¸x,jŒŲåq(VA™ßy˜…OjæÅoÕBe-ČoA˛>k+?ƒ3­°ėVĖŽœÅĢ!x÷‚ÂSŒ‚˙ øŊ[3OĪņ›ŗĶ0ú1 ÔÃ'w Ĩj×ÄėšˇĄd/âāSÁUš槜w¯Ž†px6kfäi]úĩŽ^.ÕÕ#ŊēēĩjkážÛđ…F§Sõģ dFŖ0ō,~–F eĩfž[é7ŋ}^XŗįŨ†@-xOC^#äÄAQA ƒĖ‹É‡ņ2Bų*¨ØK—iæk¯ Ŗ* ųģ ˇ”÷Āĩ\ŋ€,”koBZĀČJ<wĒáâũ°||&f<ˆqø8Fč>(o@)LiĩrmČ^[ŽŽšŊĐ¯Ä.û•OFũJ[­_ye…_ŲuJČŋ:)dî!?e×üūC;2zFČäW„^'d2)d2#¤ëz3\ō8MŌÖoUÃúļ˜Ŋ(ŗŨīĮė’PĖŽYŗĶ—,ŖĨÅQ¤‹ ŽŧÅlȞuN]s#Į¯\ōø•žö+Õ+„ŧ×rÁŊBÎ|\Hû–‰!e‡7nØ5]ߡ#™ųBŽų„íRšŪ ›ār ,9ŗgGĄ$  ; î&(\ å?k%œ{ãâMŒÔ+0lƒrŊŽowú[OÁô2đÜIp An;ønCŅ[0c5 {áŌrŒö FچlëŠēfVXH] Y6_Č{6 Yœ-äÔ%Bˇ ™ū!‡?2uHČô ˛MČĖ‹B4Û5VŸÉŌL†Öč? cp×ūĨ>ĀĀf֖ŋܡļ€fŠEW!+ė7¯oŅĖB†`ä¤30~&Ž€k7ä4€z<ŠūĖ‹ŲéušC_ƒÁŨ?ũĐ÷0ô/€žMĐoB3Ž cVØ2|*(ē ÃaHÄA$aāAŋyŲĢ™ĸ†.}ÆfÂÄs]yuā]ŖuN÷ŒÎ‘Uhë č= =MĐs>ģ=ƒĐã…ĸ0æ%-C‹ƒ/ SR“RaHvÁ ƒ{a`z—øÍķ>͘C˙é‹0ņEPúÁs ƜÖG×B't:Ÿp–¤Î4tÖ@IBKę,# )ȏƒ7 ž_MHo€‘†”ÉFH,k*ô¯€înŋŲvR3į΅á!ŗōū´åŽn€ųwáÆ!¸ÜWšāĘI¨Đ…QŊÂ2 oƒ? žĶāũ7đÄÁũO÷“ÆÂ0Úéé`7Âȓ0üeHTÃĀôlƒøÍˇĒ5ŗˇ Æē ?âˆ@w.Ž„Ķ3`ņ>aŦoŗŒâW pÚhJÜĪ‚š‚ŧ~Č CÎŨÃøoA&A>cQHoģR): ĶĄû\^í7÷_ĶĖäcŽb´tļ‡ŋŽÆŽS–Qr '!ŋ<PoA^rö‚ō]P␆ė+“Ø›a"0ņ$Lŧ™0d2ų&ȝ0öØë`pttøÍĨË4āĩW…ąc‡eŧ携”_‚ëīÁå׋…ŦČēYqČÚüa@f3d<i„ņ‡`üČš0Ö ŖŖ`7A*äė=ŋ…‹“ŋöĒ0Ū;ƒE]Pŧ´zđD@í€Ü\ČņŌŲ‹!ûup­œ)(1d3Œí„1/Œ6€}Fr Ų‰ †Ī\ũ œŪä¤ú…u‚Ǎķ/ŦƒwŽcŧË_†Ųw`ú ԁĪŪRpērĢ!'9GA‘“Ō->ļR 0\‰N°~ ŊŊĐUíTöâcÂØZ$XüC˜ˇĪ™’ķę!ũC(Ŧ‡˙9†q¤æ>3Z ¨üß_™3=g@ BŪ›W;I`¤RUôĀĐ ˙}¸Ķ]N;ÕŽT´CÅ.(΃@“3ĸÁņË;`|¸ÛĄh tĮh‰AŲ ˜ļĪ™HZ7äŧAđ4ƒÛž$l†ĄZHxÁj€ūkĐs# Ë‚Ō!(M@éLO@ á,Ę{¯ų022Ë 'Ļø ˜ˇ}g|P\…•āī_ō›Á{—@âMLåūG25 Š>…i!'ĸĸ505ū-0e 䅜BČk…)ī‚ŋF[a"J+¸[Ŋ4Bo+ÆÅÃP¸üA(x|%“ŋEqXk´ļøÍ@ĘųG>Įū™3€¸âî˙c˙ÜŲ¤đ€˙¨ehuĸ![HPĢėČôSBį 9í!!§– 9uģÁyBRģOHí)!}ĩBæ?+¤÷ëBz˙]H÷6!ŗf9z@Čą…ß+äø*!'~ $_ŌõBē*…T2gšy„Ės é>!äČėHĪ{đŋI9lĮËIENDŽB`‚libspng-0.7.4/tests/images/basi6a08.png000066400000000000000000000005511442617636000176240ustar00rootroot00000000000000‰PNG  IHDR }JbgAMA† 1č–_ IDATxœÅ•ANÃ0EŸĨA¸ģ˛fA¸›†‹MÁÁÁE*q *eØ@ՔAüHɊâ|?'ãäd€G`ė 9ģâc:1Ⓣääę{ũŋ=šk€ ¸ŸÖĩSĪ˰gž–c×. ’“ˇ3{Ÿ˜ŒÆ_m č/—N Ú@Đ-~'ų.lŋÆMđü1Ļ! jŅ  ōŧō DĮ ĩh œœųã™Ī=F`¨u@]š`а^-å^%x zRhāb!â9šĢ:XF/¸hü.䋱 Ū×ŌläŋY šŦPtėP΀ÚW(å3đÎmYĩ›Ëęm Ī€Ǟęu(¨×Š P:Ÿ‚JSôiNsBIENDŽB`‚libspng-0.7.4/tests/images/basi6a16.png000066400000000000000000000101241442617636000176200ustar00rootroot00000000000000‰PNG  IHDR Tí–!gAMA† 1č–_ IDATxœŨ™ktU՝Ā÷Å}å&÷$@h⍠¸(Ĩ"І­`ĐYŽĐŽ­"Œ ´BÎZĶ.oĐY(ZAG­Ę$ƒ¨Píč€É Čt 8B[И#CIÎIîk_îãˇŊ¯$ˏœYŗæÃ]ŋĩgī˙cī˙ī}ļmÛ­&´ZC‰mˇôIÚ6D#?ũ‰$XVIņĘÍŽŸũlĖd×ŊOB­6ˇnnm¯|°ĄÕÂļ§°,()^šųąŸÂëVn–ŖŽÜėhO*Ŧ¯”áø¨Õöí•s×L-Čđá‡5S§<í† €ī΍Ŋĩáļ:wxÃ4SEĒķõĪŨ°# 𛷍[ŋwëëŨ8Ž[ƒõūĄ ‰áuā°íö¤œ,â‡ZíÄZY:Ū ųŸĘŠÆ+Ɂ™Ļîž$n PŊnÄa¨ WķŨšP^qŗl}ĢŧcCå#Ļ˙~õˇNļZ`^ĨĩˇZƒˆš$;čØĐVWųˆŠģ§ÃŖȖG6Ü8ãũ÷Fėž°ļ=ę0@ō@× ™SžūCŽMĀpmr/Ģ˙$’ā0×J?Béö?¤;ÜØį‡ˆtKi ">SÍ­Œ•VėUõgTYŅІŖĢÜđĮ2€‘sŽAÄw厊D|§öɎũ$˙ø dÂ:ĩ|ÜsGWÁš#ߘ†å, ŋ PUõņ*ˆøÆÔԆÍ’4­C=3ĘÂQ86sĘ€SģĮ-÷ĨōÉCŨˇŒ?Ŋ"žSWkÚK/š:„÷[߯×ūÜlįų;† Аœ~’Ž-×&Ąg]ēîĒųā°í‚ãņ÷ų•Û”BŊjų+ęSĸÚwÜÔŨ—–TQ@iĒd'D|ĨŒœ_0$[]ļdÆ)ķu]Ō3ԝęģ/ŠU˙'âĮŠ&›:ô=Vú¤!``wņS Ā̜éW uTÄĘBÄWAUD|ŪUjĀՒŸĢō9U>ĄĘŨÛzį•í1učlŦh0ôĪ,9h HÔ÷ˆ=žų0H§ĀšFŌ•ô¨-á_0éŠáņMâ–ņņ-R_īûV[[ÍTS‡öĪĒĒ ąCī›ŌŊ7dgēäŽ$@~s¨9%@VmÖ‹ã”ˇŪ“´ĻKûÚÚN]]3ÕÔ5 îŋ_ļÜ˙#€igTŒ}ž čųøƒŪ™éƒžĪ3c\ģs † ”‰{$û)cnčgî*ÛcęŸ4Á„Ĩ&'[āš9Xáũ`}æŊŋž'›ŽŒšæįp˛ ĻũāŠ1gΔ>Đ÷"@čBl€Oj¨bc%Í€øą„Q4ŲÔģš`ÔR“ŽVU‹uŽžņHü3ŋŽũ› eMĐģĒ@û,đ€ÔLčÚræė¨%ĐĩåĖŦɆ¯íi(ŲĶ?XQ-žņ7õžz(m6‘Ä2€6 ú~ĨņPt2›Āŗœo@ūđũċP˛úgCî¸ÆûyČ.ƒ`$JĄ¯žg]éq0÷pUzOæƒsĶ$˙ļäáɒŋŲ%š}‚äĻŋ—Œ^)šōÉŒ’ŧc“dmXrĘS’Ÿd8jĢ -é°íÔ7!ܘŠúN€ũSĖו qŦ†pÔHšĄ¯‡@8ĒĩfÍ´Čnâ]Ʉ*ģ­’yU.ôOŠúø.É~Uß×Z˜¸Ī_š4ô÷•”‚Ãļ*Ãpņ2ˆø ôIˆøbëŠĻ€-D˛/.‹l_YYšŗ¸0ņų–‘ĩ†€ūE%; ņûŠļ)7˜‹e‡ô‹’›BkGD!-7KJ@‹&ڕüŸI:˙MiŽęãŠŊŠFaâÎPŀ! oyéF€¸ŋ(ÔQp\rs@×Iš{5­w„Ŗ•Ŋ_8*é-™Ú#yūfÉvCÚÛĢ"ŌŖ”æôʒ–*Ā%Āš&%ĀiÉOšĘë:6@8™-'đž*ŲS,ųņęÂįš´dH–Nčn-¯čõ—ĨL]‹Â 8¯tõJēo“ėūŊ2å•ã6VĩA8:jgM hŅwî*˜úOogčY=|=@jˇ>@ĻØ3mqĪČœ2čN)@Áö÷UĸĩŸ•Ėŋ)™Ų*ųŪK ŊüKGšká;˙ÁŌaÔRé<īĢéģ\š/œĄ| ĀŅbĪpøí:CČ=Ļ&ēR2}F2Ž\r~Œä‘_ŧüÜ˙W†¨™úÚŽĒĢĐێŽ›5e:DVë[:€ŋ850Ŧõb-€{lö4€KĪ uAzģ¤PY/V.yA p˛ āH%L;kˆÄž…Ē*h˙ ēgŸZ1=‚ŪņLlî¤ÛĐĄ˛ˇŖ \m´$įø–‰M=ķ_ī‚/á™ÁŸÜ ×,5Äųą0ō´!2[ÁsŸ!Bˆ}nˆąÛāôŊ†(ÛĶŊĪŊÉÔĄŊ}č"´ RP؃\WËÉzH˛ëlAŒ+ÆâÂ50â$Ä„ĸį!˙8oo ŌEū3°Ž@Et6B勤Žpĸ÷d‹G CēÕ[ PHN> ÎK.ēš`ÔCôŨĨÛ +‡Pˇ!Ōŋoĩ!ė_€ãaC¸įAö-CLĸäčŸnˆQ¸ ĸhrė Į-Ļ>ČūÁė‚u1†¯5„õ„7"v„Ū0„˜ž ‘ žĪ a{Á‘6„Ģre†đž€ôDC÷BĸÎ%[Ą˙>C”> }"03a8:LũR ē,˜õ í0D=”4C܆"¤>˙$šDŊ÷@n1¸v€]Žf=ōgĀ3™bđ1/BōĮPŧv@ļÜ͐!ÕXڌ.&Ú„AƒÃļ g>ß7ŠjYJ} ÅÄ˙^ŋ˙kũ ũ@´øj­‡mŸ›&+ŧceVöUƒŋáhō€lMTŧŦœøŠúBų+Û ß˙o˙5ķ@*ęo°t'|ÕĻéĶŪąVƒL]v–Q†PųÜטå_ Z40+XáhüVÕû˜¤{̤ë#I‡ĒĪ+f3*ŗ#-YČ;…;ɗi@Ŋ^ĸrBĄXĨ¨bVŧĐūQAáD{0béÚá_lę ž$@ēŪÛ pŅ3,n°”<Ÿ(nP†PõŪ‘’ū(šė-Z4%´ÂŅū~eE—"ŨĘęšųĻR¨R`–2€J¸Yõ]Z1Н é¨_ĸ0ߗ(([Zcéė2uHåü.€tŖˇ ũ´w%@fąg@f‚į„”ĸȖ‡ŠŗŦˇCŌ§ōEā @II(Z´¤DĶ 흧žš¯UĖ) EįĘ@ĒZÕĮ˙BŌRåžËØ;ŋ °ijaK‡X,2uHÄ߀tĨ”øbã°€LĀ“ČÖ웞î~{Č p*_|čVpĢסē§;Ŧ ĸ2ĩ_Ũ:Š>(ûux hŅ2Ęë  ĒõäR[ }Ĩd˙}’…#VLõ;¯.§÷*>ŖômęŪ[>×ŌÁ:cęŸ,gLĨ¤é7pąiØR€ė )qvŸ{.@ö÷šzW3@ūaįŗCV€S%dĮ6UVpŪ TPu×uĘ J%z–ņĒ#Œ?&ę¨|tøí E+ˇÂQO›l5•jÔvyÚԑN-÷œĨCĪ›Ão7uˆ•ĘS!9Cú¯ŊĪd2R‚lą{ ˇÅĩ ×â’ĮßÃÎéų€3`/vėČot.˛PÔpŦSåGUY)ČUV1ÃáÚîü­2Ô ĘP§”^¨ŠŅ4Đĸ55 A8Zø^ÛšđNKĶÔ4S‡ô)ī8€ė÷Y€ÜƒrÄüõrûMĮۖcØ×;ä5­‰ĨvšCÆĄ R{‹c‰Ōŗ¸t/Ŧ{ŧĸō¨­V€ŊS2¯|•W×ÎŧÚšœdvŗdæW’B…šB\>˙˛dÛQŠ.w-ŧĶŌŪ Âkģ`á"€šŠmm#ÎM–ŪˇJ<āųK9ƒÛʖ¸\Rįœ|+€sK~ €ķīō8í¸tqÔÛ͎ģmČ ČĢŊžûGUVČ)Eŗ*aeWTõi•ĐRJҘē×ôuJvė8ĩÆ=g韮€ņĪYtmQKaÔ čznųŪ™ŸŽh;:~+úŠPŗ ōļŽˇJW÷­=)gđONđzĨžâĖ€g}f€ģ%;Āõin<€ŗ9_āēUjčĖįCV@NíÂŦ jYe€Ėhɋ*‘•PH$$ûՐqē{/”ĪĩôîŊP^gŅŊĘgCÃŖžŧ¯Ę_ún>=՝ îũXÉUĐŊˇã™ō:ôî}Lz lĖ4%÷?  J |!đnI/ö;)ąį ŠÛŸM¸ëŗÍŽĒ\ûŲĢXČÜĘéī+…ÕŪNŪ§W™Ø4™¯…-Ũ4AĶ,Ė2ĐzÁŧ ´wÁēÂ˙ ŠŨāŸ9\¸* ÷…ŦKí†đãR"Û‡2ĨŒ@ĸLŗwžö+tĶęĨJ()–g‚`qb °%šĀ!īëRĪžĖ|O}ĻĀS—Ų+¯cʅkYá1í‚ ‚įÕÛV—ō°|jŠ­ë­ąôÎT ĸ{;”ßcˆŪ™PvĐÖ W"žŠ6"åÖ™ÍāųĄ!ōׁķCC8?„üu†đü2› áĪBĘcˆĸM_nˆp5X' QvzgĸüčŪnˆŠč "´ĻĀųŧĨCgqELĘ9jŠ”[^˙.ŦąVęĨ^ck;`ĐmŦ°Ō+•§Õ jČĶtĸ‚KOŒX$F@đ$–Cp#$Ap$*!ØÉÕr§ đGāb; Ģ‚Ŧ.ßūí⍖?û„ŦËę0Ė€‹đ×@Ē r¸"`āˆ€sä{#d—ƒ÷¤G€ŋR-0â#č #ÎČ)Œü˙Ä÷Oâ&_ŊhđęéčP¸ËįĶT4yĐß`é}~(MÂÜ ÚCô/‚’†ČBąÛņmPt¯!7Cđ]C¤Lđk†›ĀˇĖ™ĀsØŲĶākˆüā\c{"8ŽÂq뉆pށü†p…ėiCxCæCø–Ødŋ)ĶÁw!qŗ!Šî…ø6Cģa kˆ’ĐŋČÚ0wĸ4 }~Cø’,úĨ)ŠgiķĨā(/āšīdrs~='3_ÎâķZwˇëÎt(pĸ3—ķíōp]Ņ`힁_čėōytϰĀ?tĻä(Cį\l5čė2õčLģčĖŠg  ķI°@g.`‡Î(`øĮîļgį ôDgī&ĐŊ›Ā(ô(`÷‹Îû60ŊY`zŗĀlô(P4Đ9¯Ü€:{ą€zą€*zȟmtÎ3đ€ÎžøAOŧĄ3ŋæBū^ģÄėIENDŽB`‚libspng-0.7.4/tests/images/basn3p01.png000066400000000000000000000001601442617636000176320ustar00rootroot00000000000000‰PNG  IHDR I´čˇgAMA† 1č–_PLTEî˙""f˙lŌ&IDATxœcā4â ¤ģ?Á“‚fYIENDŽB`‚libspng-0.7.4/tests/images/basn3p02.png000066400000000000000000000002221442617636000176320ustar00rootroot00000000000000‰PNG  IHDR ’ggAMA† 1č–_sBIT|.w‚ PLTE˙˙˙˙˙e?+ē"IDATxœcøēŠ„đ0đÉÁĢ ,| ¨Ō˙øƒĖ=ŊIę‰ŽIENDŽB`‚libspng-0.7.4/tests/images/basn3p04.png000066400000000000000000000003301442617636000176340ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMA† 1č–_sBITwøĩŖ-PLTE"˙˙˙ˆ˙"˙™˙˙fŨ˙w˙˙˙™Ũ˙˙ģ˙ģD˙˙DŌ°IŊGIDATxœcčč =sfÕĒōrcãwīfÎd C•+(HŽ*×ŅTîŨģä qČ@å*)‘#€ĘMK#G•ģ{7āÄęŅ‚}IENDŽB`‚libspng-0.7.4/tests/images/basn3p08.png000066400000000000000000000024061442617636000176460ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_PLTE"Dõ˙íw˙wË˙˙ :w""˙˙˙""˙ŦUf˙f˙ff˙˙"Ü˙˙Ė˙™DD˙UU"ËË˙DDU˙UËË3˙ėÜí˙˙ä˙Ë˙ÜÜD˙Dff˙3D"íí˙ff˙¤D˙˙ĒííËËū˙˙ũ˙ū˙˙3˙3U*˙ˆˆ˙ĒĒDˆˆ˙äËē["˙"f2˙˙™ĒĒ˙UĒĒËc˙Ô˙Ēw:˙DDÜkf˙ˆBė˙ÜkÜ˙Üē33íís˙˙ˆ™J˙˙w˙ƒ˙ēēū{˙ū˙Ë˙™™"˙˙ˆ˙˙wˆˆ˙Ü˙3Ē3˙˙™™2f˙ē˙D˙˙˙Ē˙wūūĒJ™˙˙f˙""™‹˙U˙˙˙˙ˆ˙U˙˙ū˙ũū¤˙Df˙˙˙f˙3˙˙U˙wwˆ˙D˙w˙˙ff˙˙í˙õí˙˙˙D˙"˙˙ííˆ˙˙w“˙"ÜÜ33˙ūūēē˙™˙˙33cËēēŦ˙U˙˙Ü˙˙3{ūííUU˙Ē˙˙ÜÜ˙UUfÜÜÜ܃˙ww˙ūū˙˙˙Ë˙UUwwūūËËū"˙˙"DD›˙3˙ÔĒU™™˙™™ēē*U˙ËË´˙f˙›3˙˙ēĒĒBˆSĒ˙ĒĒíēē˙˙ūD™™™™˙Ė™ēˆˆÜ˙“"Üū˙ūĒSwwË3˙í˙ē˙33í˙í˙Ĉŧ˙wĒff""Ü˙Ë˙Ü˙Ü˙‹ËUUˆ"˙˙Ë˙Ëí˙ˆˆDD[ē˙ŧw˙™˙fē˙ēwwsíū33ē˙w˙DĒ˙Ē˙ūū""Ä˙ˆíí™˙™˙U˙"˙´f ˙Ü˙ēē˙˙ˆ˙ˆ˙3˙šŽĶbąIDATxœ ÁĀG¨ŒHd+;3 ekXegīŠĐ°÷**4h˜ ĢlĘŪŖŒ†•Y2˛÷žģ˜Â÷æWú˛—ĻÜ%äsƒžŧÅyŽiHÉCLå*;8Ā"éųˆŧKE6sŠxš'HKđ?ĶYÎâ9ōķsčÄŖüČ>ĸ<ÅČN3ūĄ7_˛x•2L&1­8ÁP~!^ãŠđûŠGwÖM¨Ę,ÚԘ´g%҆<ĪĪlŖ YHŦĻ(#9ĘV–pšŲÄ&Jō8S‹ÜüŎÄNĘņ/ą–÷YĪ×Ä!*“‹˙¨Ä%ķq’|ç,Ŗ5ß1œx“j<Å8*p¤ n˛‡Ĩt ¯ĐŸ¤D;Ö1a1ˆ~ô!ŋ“‘'ŲBœŖ ÉËō0šÜū‡–ČāIENDŽB`‚libspng-0.7.4/tests/images/basn4a08.png000066400000000000000000000001761442617636000176320ustar00rootroot00000000000000‰PNG  IHDR Ųs˛gAMA† 1č–_5IDATxœcüĪĀĀ…œh41"œ,ß(,?†žŖa0T1`4GÀ*ŒâhPÃß* }Äë…ČIENDŽB`‚libspng-0.7.4/tests/images/basn4a16.png000066400000000000000000000042361442617636000176320ustar00rootroot00000000000000‰PNG  IHDR ‰ãn<gAMA† 1č–_UIDATxœÅ—_h[ᯖ¤ŖcëøHuRGTŽ(8…ēBē-ąŊŽPŌ.%4ģČäĐBiKa‰w‹]Hž˜“‹ĐôĸɐX°ƒf%…$όŌz„Ōâ•mĄięJ­âWG‘ícũ9–wņ~Ąt×5™OĮŌû>īķ>Īķ`eaX‡=§`ԂÎÂą8ūGČÁŲ?ÃÅ]đŪeøč øį_áî^Xŋ.ŋw÷ĘŲGOČ3wÉ˙d‡ä3Ž ČgŽZōÃē|'€feĄf[šŽđĄģZģ!pô&„Ū†æuhÁĶ`c:Īoȇt’ræiĐ^g›)XŸ÷Ŧ]‚Uꨧ V€Z Ŧl-Ķ5ŦC­`åœT$uá!Z˙?¯?pđ߅ž÷úCgęĩj@dÆą­t-ŖYYéŧ˅Ēae}'Ą;ūĶȀ~BčÉ@3í x'Ą“ÍŒB ^ÚSĐĖA#nÖr°2õÔrāä :QˇfGR`fA‹dvŸ ž“°<Éúæ@˙— _„Đ"ôŒAÃƒÖ‡ā†Ã°ųˆ°qXÎZĘ3îS°ö:Ŧ܀{ĮĀŲՃ°< ũ';âBß ˜1Å­Ú/¤sß,ÍZYßŖ íƒ` ôŊ`pgĄ=ũ°ųUĀ<´¯@#ëŋƒÕÔ× ö/¨ļ`ų6,ÍÂÖҚ̀Uķ]čŊ@`7ø&°kcā{ ‘Ŧī ø?€ā %Ÿ€õ:4ßo6¯Jۈœ­ûaõÔKāŧËGaɄÅ ¤ģ?‘ ˜¯@¸=ķ Ā!”eæū/Ĩsߨ˜VV‹CpŒA‚5AhīÎ Đ.AŖk%¨˙œ,—ā?%¨”!V¯Ų[?ƒhúv@ø[č™ã/ Ŋ ú!œ~Q`÷ZJåHVĶ@ץˇ, \Z-čt¤€VKÎęu¨Vai *(•a0îØץŦ „ =i0ŠšV„Ū†Đ.a{hQf\‘Î5 î|eeũûÁ8 Öl9QčüF häaeĒXĖC9wnĀöáš‹A˙ DŌ`ÎÉ6_B¨Áûh^ãSyŗgLg”v]˙~¸•dƒA_ ļlwŧSR€; ÕI¸[ƒ…áVF&;ū5lŨ ‘:˜ĐÛFô$g 8ŽhĄ—=oxÂöđ ™yo¯t| æ'­ŦQ†ū2 Û,ˆĻÁ4Ą§Ą4čcø9øŸ4O!°1×Dáŧ͞į͡„íŽ+3߲M:$āęm+ß?=*|~Žž5{Wb§ zÂC` ÁWÁ´ã Ũ„îčžĨč< Cäu㰈Œˇ"ĢÖj áÜQ}8ņũpî|$;>.œ;/ŋäØ—a  Ö4ŗ /@ ūĐŌĐŊ[‰Ũ/Eū}ī)x6“ĸ훏ˆÂm^•=īt„íŪ)™y#!Ã‹GÄT/ŊSŗˇo‡ž  ūS M€īOā큝]ĐÕ]#Đõ7č*gĀĮüŖÕlņķŽ!ŽļqX´Ũ…kĩdĪŨQaûâ5™ųšķŌ9Ā‹Gāå—āņ2 ėë„Á5´ŧÁe5‚$ølĐûæ ¯(–„„+ŗ˛į•¸°ũęû2ķíÛĨ‹—_’‚$`Wb“Í?@Â0 Š„9EÂį {Bāk8ĨÖ0/Æ˛VRōZ‘ųâYĩ‰šũxYfŌų\Ŋ Nvūę{k¸ ByĐ_ƒ@S­aJ­ĄcKŒj…T˜đ”ĨžcŠVE^>‘Ų[vė]Šíú7RĀĀ>é܉Ã\Üi¸÷ @ī)Ũ]SB”˙B yšŸĒ$ķ”ōķ’¸ÚŌ’hû­ŧ(ÜÎ'eĪ­ia;ČĖc“Ōš; ķ“Đœ÷kX]…HĖôžÆM%ÅI%Ŏ­äīUŒz]„ķ‚XjĨ"Æ22áØCO+hī É´ )˜•™oŗ¤ķæ„܆F\Üŧ2Ŗ7•Ĩ h+Ö'Á Š wC’ĖōQņķRY\-ūĩ‚4-äŌdĪAØ’÷.Hįía¸ķx4ÃĐ,@Ŗᜲã$„îo{ÖĘ ī“ĩdJ˜Œ;v,&Ž Š CÖ˧T$3Ķ”gVWĨsĪ“<Úyh ąßB# FP!°v VŪ”ôęl‘ ˇX$ķp]ųy]\­§.Úî_…Ųs=,l7GdæŽ){&TLč¤ÁK(į}Z)hÍ+V ¨Ģ%@¤{ëg’d"iåįiqĩāĢĸí]ŠZ^ö<”ļ›™yŗ wŌŌPgŧ+ĐŽB; ­įõ Üs%ˇ/Ģôڟ gU„<Ŋ=ĘĪĮDPēw‹ļƒüí?({ē!l7įdæÍ€tŪ•Æ:9Øø-xIđb z œš4ôŸtėhFŌkß•á2*É<#~ŽWëQ\… 4eĪ›ÂöpNfŪ.Jį,OAĮ†N 6løņ¯fŽ-ÅȌcGRęÆōŽĘí3*Ŋ%Ê’dēÕEļĢ(ø’ĸíū”(\0){n$…í­”ĖÜKJį›3ā¤`3Ģ8`ek+-w53&7–žyÉíĄiI¯Áq•áŋŧŦ4u‹-⋋=wTK°\é*Ŧėí< ĐĖeę7 ņÆÂMŪ@xÎÛŪļ"õ& ŋnܛ9åfŗ›5ęų|>ŊåäIEŊtSqEŅU´vĒvčhÆĢëķ Ŋ^R\TŦŽâÕjžˇV‚z=—XŠtšodöũū€ÕɎa€pĖˆ~î~ špÛ}€c˜‰´Æ ę׍{3§ÜlvŗæB}ŨÛëŪ†|9:§„^TčĘyJĐÖû‰zõđ5%ūW‚õįkÚīF‹øëÆĢ%X<Ņs`yšĢ  YÉ‚Eŋ`uĩŖ < šs7DOģ¯ÄNģl] #aL$7ÍøuãŪĖé4›ŨŦšP_ŋ}ũvč-‡sjõŌĩ˙ÔˇÜâqšM%Žq~áSÁĢīâWĪ­¤V‚ÚHī8ĀRĩ;ĐhdŗÁQ˙ĀęCįĸ‡Ũ_D—Ü{ĸSî>€¸Įų ųgûoÛ|€­§Øh7#aL$7ÍøuãŪĖ)7›ŨŦšPī?ÔōåϤĒ*>ˇWÍU4^üúYÁ+/âWŽ÷ˇZ‚…Oûî¨?—{ 1“ß+^ |Á{ šwâß ƒä ûu€ø'Â0™°oŲûh5ÚÍHÉM@3~Ũ¸7sĘÍf7k.Ô7žŧņeč-/NKë?ĻæRÂ×îŧxĀŋ¸ãÉZ ޟŊs'@Ŋ/ˇĐøeöA€āY˙@Ę ĸŠ[ˆ?pˆŋė| œˇHÆėQ€tŗu yŲŪßfKĪŧQë-ŅĒÚÍHÉM@3~Ũ¸7sĘÍf7k.ԇ††† _ž|IīV§?4ħ§‡îĢ–āځģN,ē'š[3“ákō†¨ęæâXf|ß>Ŧŗo$_ĩNYÃé%a”n˛æŌ Ģ}đMEûF­Ņj´›‘0&’›€füēqo攛ÍnÖ\¨ī~|÷ãĐ[~|!~æÍŨģj%¨Vķy€āĮūN€¨&#ā“HFėq€ôŦõ@šĘ ԊUā;2ÃtŪ`Š@úĒ2zKū”oļųTÛĩō3=-ĩn´–j7#aL$7ÍøõVž¨§ÜlvŗækÔe„ŨģÎŧ ĪK˙@pR (#8§âgėrR°KĪX–4‰ŠÉLšĸ;ˆVŖŨŒ„1‘Ü4ã׍{3§\6û™7a÷ŽZi÷.ųæ­3oí~`čžéi€ģŪģļ  {pi 3ŲÜ ā=)opĢQĀqdv%)Ø3É{>°ĻŌ€5–ŽXaęXiûHŒ4Ҍŧ•˜šüˤ+ĒÖ[ĸÕh7#aL$׀fü珎éiē¯ZĒV!Ÿ—_ĩ C÷ÉŊé§?Øxōâ~€;wĘš1[oäü’ zžĖĀ­DEį™Ą3ؓIĀ~]Rlë‡ÂĐ~%yēŨZ+1Ĩ“‘ˇS5€ISZjŨˆVŨF˜H.íâ~Øx˛Vēvî: ÁÁß ūNÂ]īÁĩm°ņ¤ôŊxā⁍/4į3}÷Ȉšå ŲÁÆ €īË ŧcá÷ŧįÃÜËŅ'ŠŨ› ālŽ/ØIbˇ×*aSƒ1š€Qų&1Ŋ%?3jŨˆVŖŨDÂ\9ũß­–ŽŸ…;wÂRē'Å3¸Ŋō‹jĐ=K3ŌįúY ž™¸ōŌ•—ú„xį‚ {vi ›•ø• Đ1ģēĀ\[¸ûĸSÎĻxĀ>‘lĪÕ´ŠOĻcJF Ū’Ÿ™4Ũu­WĪÁúGjĨ…OĄī9šhn…Ė$Ä8‰ņ3ŌÖÜ*}ę}ō˧RúōÎÁÕwŽžŗ~;@4îîßķžk’7s´yĀī‘v|´úīaāŽE%÷ačLÄí> úļĸÉô ˇj0&ŋ3šÂäg’ĻÜØëÆĢĨÚôŽËČŊ_BöAq‘Ū“Œ€=v’’´…¯Aļœ&fįūolnūũúũĄõonų4–ŲÖ|ˇ6j™dļų.dļ‰nÆÆdoĶūGq˙đ?eØėíÛĩķIENDŽB`‚libspng-0.7.4/tests/images/bgai4a08.png000066400000000000000000000003261442617636000176060ustar00rootroot00000000000000‰PNG  IHDR Žt‚égAMA† 1č–_IDATxœíÍ ƒ0 ĮôÖŊAû(öÁ÷(sO°íäĩĸAV†˜Ō˙G":ÉŨ™bŲH$ ķ€@œŗü Ą›Éžđ‘čzMŠ2x<7UĨ[0tÔ<n!šYģ~.÷ļ,>āR¨fqXAhžŲĒwΤ5Ŧ0o0N°Á N¸6ø‡ Ž ŒėO= YU]IENDŽB`‚libspng-0.7.4/tests/images/bgai4a16.png000066400000000000000000000054471442617636000176160ustar00rootroot00000000000000‰PNG  IHDR ūä^ĒgAMA† 1č–_ ŪIDATxœ—pT×uĮ?Z=IowŅęíŽB2Oˆß„ „5ĐÔf ãpҚfĻ4Žbôœé8OÔ5vH\âŠņ ÆC°c7–ãÔŽ#-+06P‚ Ђ˛´zW+íę­tĩęOL2m<“?žsÎÜ?î|Īšį|ĪšŲæLøÍë`×@–9ŒĻ>‡%K,ƒ‘ˆ›:Œ´ ë쇞íøö–ĒįoیÍŲ! ~>6€K÷2m4ä?ą)¤uü7,㍝ĘË€Īö‡4uže–žqCYßÍcgg'âĘČ i!˙lņBč4MÂŅ\xË2s&ô߆OŸiëŌŽî‡Cģ-ãŅhDđëīÃŊŲgëŋtĄ<ĐKz åēzę)]}į<”sļ~éÅû@šŋ ¤ ցBWTa3ŸFäÍe”ˇįĀĩ ;FŲŲúŸa_ß ĩ{ Ŧ”ˇįBKŦ:tļ~ŪWžN8ķ 4ũ Üŋ˛)ŨŊpfnėƒ}Nrîæaė‘˜ĨĢŗu÷ė~˜˙˙Ļ =8‰Â?ņƒ0ŅlJHƒžßÁ͗tõ7 ēÚ{ FŗÁ3%CP`CŋĮ‰ããø¨2[ÆÂ‚˜]úŠe+! í0ā…îáĘ6]ũYĢŽ&Š`ú4đ?įî8IH˙Â2VÄėō[vQ=4Ō@ļÃX>¤?‚ä30°.—ęęWßÕÕ×\Z†QŗKžiŲžnp7Bn(‡Aą í;Ãë` n~"OÎĮ,Ŗĸž Ū:XÔĶ@Áapŋy(b.ôŊ7ŋ ž9Ģ-#ė˛Xŋ1s;úWhÎĀūŨPŊfīéS!¸˛6€î°îîØĻĢŽrÍŦŠÂ’(T€Ē(o‡ķ+áܟā| ĖKÆė˛WcļÕfŗŪļ¨Ü€˜ĩ fCņ>đ„AŲ Úy(k†ąfPŋEMpũx¯˛4tõķ! ˆŲŸ%k–Öx3ė7ķâ0%ū8$ûÄNÄAū-Œ!„xz‚Đ„X!čjlÄ5+ .S3;ļéę-[Wû~ĸ̃ķuÕ>ĄĢ™Gtu,_W“ßĶՁëēÚSĢĢ7_×ÕĢ…ē:ģßa¨Tn÷¸ZŽ™—øMßŗ0íļķ¤™cöõt—Âĩv¸x,jŒŲåq(VA™ßy˜…OjæÅoÕBe-ČoA˛>k+?ƒ3­°ėVĖŽœÅĢ!x÷‚ÂSŒ‚˙ øŊ[3OĪņ›ŗĶ0ú1 ÔÃ'w Ĩj×ÄėšˇĄd/âāSÁUš槜w¯Ž†px6kfäi]úĩŽ^.ÕÕ#ŊēēĩjkážÛđ…F§Sõģ dFŖ0ō,~–F eĩfž[é7ŋ}^XŗįŨ†@-xOC^#äÄAQA ƒĖ‹É‡ņ2Bų*¨ØK—iæk¯ Ŗ* ųģ ˇ”÷Āĩ\ŋ€,”koBZĀČJ<wĒáâũ°||&f<ˆqø8Fč>(o@)LiĩrmČ^[ŽŽšŊĐ¯Ä.û•OFũJ[­_ye…_ŲuJČŋ:)dî!?e×üūC;2zFČäW„^'d2)d2#¤ëz3\ō8MŌÖoUÃúļ˜Ŋ(ŗŨīĮė’PĖŽYŗĶ—,ŖĨÅQ¤‹ ŽŧÅlȞuN]s#Į¯\ōø•žö+Õ+„ŧ×rÁŊBÎ|\Hû–‰!e‡7nØ5]ߡ#™ųBŽų„íRšŪ ›ār ,9ŗgGĄ$  ; î&(\ å?k%œ{ãâMŒÔ+0lƒrŊŽowú[OÁô2đÜIp An;ønCŅ[0c5 {áŌrŒö FچlëŠēfVXH] Y6_Č{6 Yœ-äÔ%Bˇ ™ū!‡?2uHČô ˛MČĖ‹B4Û5VŸÉŌL†Öč? cp×ūĨ>ĀĀf֖ŋܡļ€fŠEW!+ė7¯oŅĖB†`ä¤30~&Ž€k7ä4€z<ŠūĖ‹ŲéušC_ƒÁŨ?ũĐ÷0ô/€žMĐoB3Ž cVØ2|*(ē ÃaHÄA$aāAŋyŲĢ™ĸ†.}ÆfÂÄs]yuā]ŖuN÷ŒÎ‘Uhë č= =MĐs>ģ=ƒĐã…ĸ0æ%-C‹ƒ/ SR“RaHvÁ ƒ{a`z—øÍķ>͘C˙é‹0ņEPúÁs ƜÖG×B't:Ÿp–¤Î4tÖ@IBKę,# )ȏƒ7 ž_MHo€‘†”ÉFH,k*ô¯€înŋŲvR3į΅á!ŗōū´åŽn€ųwáÆ!¸ÜWšāĘI¨Đ…QŊÂ2 oƒ? žĶāũ7đÄÁũO÷“ÆÂ0Úéé`7Âȓ0üeHTÃĀôlƒøÍˇĒ5ŗˇ Æē ?âˆ@w.Ž„Ķ3`ņ>aŦoŗŒâW pÚhJÜĪ‚š‚ŧ~Č CÎŨÃøoA&A>cQHoģR): ĶĄû\^í7÷_ĶĖäcŽb´tļ‡ŋŽÆŽS–Qr '!ŋ<PoA^rö‚ō]P␆ė+“Ø›a"0ņ$Lŧ™0d2ų&ȝ0öØë`pttøÍĨË4āĩW…ąc‡eŧ携”_‚ëīÁå׋…ŦČēYqČÚüa@f3d<i„ņ‡`üČš0Ö ŖŖ`7A*äė=ŋ…‹“ŋöĒ0Ū;ƒE]Pŧ´zđD@í€Ü\ČņŌŲ‹!ûup­œ)(1d3Œí„1/Œ6€}Fr Ų‰ †Ī\ũ œŪä¤ú…u‚Ǎķ/ŦƒwŽcŧË_†Ųw`ú ԁĪŪRpērĢ!'9GA‘“Ō->ļR 0\‰N°~ ŊŊĐUíTöâcÂØZ$XüC˜ˇĪ™’ķę!ũC(Ŧ‡˙9†q¤æ>3Z ¨üß_™3=g@ BŪ›W;I`¤RUôĀĐ ˙}¸Ķ]N;ÕŽT´CÅ.(΃@“3ĸÁņË;`|¸ÛĄh tĮh‰AŲ ˜ļĪ™HZ7äŧAđ4ƒÛž$l†ĄZHxÁj€ūkĐs# Ë‚Ō!(M@éLO@ á,Ę{¯ų022Ë 'Ļø ˜ˇ}g|P\…•āī_ō›Á{—@âMLåūG25 Š>…i!'ĸĸ505ū-0e 䅜BČk…)ī‚ŋF[a"J+¸[Ŋ4Bo+ÆÅÃP¸üA(x|%“ŋEqXk´ļøÍ@ĘųG>Įū™3€¸âî˙c˙ÜŲ¤đ€˙¨ehuĸ![HPĢėČôSBį 9í!!§– 9uģÁyBRģOHí)!}ĩBæ?+¤÷ëBz˙]H÷6!ŗf9z@Čą…ß+äø*!'~ $_ŌõBē*…T2gšy„Ės é>!äČėHĪ{đŋI9lĮËIENDŽB`‚libspng-0.7.4/tests/images/bgan6a08.png000066400000000000000000000002701442617636000176130ustar00rootroot00000000000000‰PNG  IHDR szzôgAMA† 1č–_oIDATxœíÖ1 €0 Fá'dhOĄ÷?U!ÄŨÅExRčP(üM(؊0^{~œ˙ē3ƒuGʨų X N5"}€\‚Tã°B\Ü.y ˆ’˙â 6 {@”<ŲP6@đR œ¸LIENDŽB`‚libspng-0.7.4/tests/images/bgan6a16.png000066400000000000000000000065531442617636000176240ustar00rootroot00000000000000‰PNG  IHDR #ęώgAMA† 1č–_ "IDATxœŨ™_l×uÆķŗKrM.åZŽK Ŧ‡‘Ę´ÛØÆR‰ËMˆ Y.RÔ2Z[Ɋ”^qũb,ĩZɊ (0PĀrŠļåBĄŅ&ˆ\Ķ)‰(‰šD~D4•K¸K/ÉŨáüëÃ9wŠUU4AßΝ;wîwîŊį|įŒ•Ļi Đ|šíؘ¸}ûË}+M˙!3ß€Ūąæ&}đ˙Áææß¯ßZĶšīfļÕFŨ5[đ,@f.3ųrZ–ÆtL01ø7‚ņqÁHÛÃŋtõÚŅįo Z?TÔûėR4īŲĸø×:ūOõ~ôô-ī{Dņ`đkEŊßU4×Ų"ž)sĄZ‚æHfn2@Ē6ęŲRļŊåtX‰O)Ū¯;ŦŌvoR'¤Ŗ¸ĪÚzßÖûÖŊŠOéÛ´=5ãßŖøDûø‘ŋOqD Oĩc3Ģø¨`cĘo”ŗŖĩ47eæ‚ ¸k§$9ĻSQ› õÎáÎaȗĶ~íõU5€Ž`ô¯Šķ:ąOÔļ ķE5ĀCJü :Ÿu'Ĩú|ĸ˙VĮMtÜ?\ũ3%ü'ŠĪ*áËJSŸ_™7ÄW&; Õ4Į2ŖÍŅĖ@đĒ˙TÛHtíZ›]m'Ôģ6tm€Ūrē¨w˙K'Z׉^҉~¤›QCüģbV Ąí–bĒí‰^ĮŠ‘Á‡•¸^ĢJøēŽ•æ‚ār‹øō厁Z SŲŦ Øā_XčØÖf€xŸNEOšŲėfͅz÷–î-/§?Õģ§uĸ5€ŽDp^ĐĶvGŅV—ˏŋŖãčũøG¯W5|ķ %ŦíËŋ\š5ėfēĢ%XYčėhNf†‚ßø÷ŋö˙ üˇ¯Ũ§uSšSn6ģYsĄž;’;ŊåäīÔ:Ápë-ū'AW¯íŊ‚Öû:š…äUGĪpxø–qūB‰ëõōŋÖ?7ÄëGsÅZ –īîú  g€`Ę/ĮüÃáfī@ø%īã6mWÔ#Đr?zĘÍf7k.Ô{zzz _NôŦF‘āĒŲĒ AĪt5€ĨHutŨĘa¨ÄÁ†>ŋŧŦ4u‹-⋋=wTK°\é*Ŧėí< ĐĖeę7 ņÆÂMŪ@xÎÛŪļ"õ& ŋnܛ9åfŗ›5ęų|>ŊåäIEŊtSqEŅU´vĒvčhÆĢëķ Ŋ^R\TŦŽâÕjžˇV‚z=—XŠtšodöũū€ÕɎa€pĖˆ~î~ špÛ}€c˜‰´Æ ę׍{3§ÜlvŗæB}ŨÛëŪ†|9:§„^TčĘyJĐÖû‰zõđ5%ūW‚õįkÚīF‹øëÆĢ%X<Ņs`yšĢ  YÉ‚Eŋ`uĩŖ < šs7DOģ¯ÄNģl] #aL$7ÍøuãŪĖé4›ŨŦšP_ŋ}ũvč-‡sjõŌĩ˙ÔˇÜâqšM%Žq~áSÁĢīâWĪ­¤V‚ÚHī8ĀRĩ;ĐhdŗÁQ˙ĀęCįĸ‡Ũ_D—Ü{ĸSî>€¸Įų ųgûoÛ|€­§Øh7#aL$7ÍøuãŪĖ)7›ŨŦšPī?ÔōåϤĒ*>ˇWÍU4^üúYÁ+/âWŽ÷ˇZ‚…Oûî¨?—{ 1“ß+^ |Á{ šwâß ƒä ûu€ø'Â0™°oŲûh5ÚÍHÉM@3~Ũ¸7sĘÍf7k.Ô7žŧņeč-/NKë?ĻæRÂ×îŧxĀŋ¸ãÉZ ޟŊs'@Ŋ/ˇĐøeöA€āY˙@Ę ĸŠ[ˆ?pˆŋė| œˇHÆėQ€tŗu yŲŪßfKĪŧQë-ŅĒÚÍHÉM@3~Ũ¸7sĘÍf7k.ԇ††† _ž|IīV§?4ħ§‡îĢ–āځģN,ē'š[3“ákō†¨ęæâXf|ß>Ŧŗo$_ĩNYÃé%a”n˛æŌ Ģ}đMEûF­Ņj´›‘0&’›€füēqo攛ÍnÖ\¨ī~|÷ãĐ[~|!~æÍŨģj%¨Vķy€āĮūN€¨&#ā“HFėq€ôŦõ@šĘ ԊUā;2ÃtŪ`Š@úĒ2zKū”oļųTÛĩō3=-ĩn´–j7#aL$7ÍøõVž¨§ÜlvŗækÔe„ŨģÎŧ ĪK˙@pR (#8§âgėrR°KĪX–4‰ŠÉLšĸ;ˆVŖŨŒ„1‘Ü4ã׍{3§\6û™7a÷ŽZi÷.ųæ­3oí~`čžéi€ģŪģļ  {pi 3ŲÜ ā=)opĢQĀqdv%)Ø3É{>°ĻŌ€5–ŽXaęXiûHŒ4Ҍŧ•˜šüˤ+ĒÖ[ĸÕh7#aL$׀fü珎éiē¯ZĒV!Ÿ—_ĩ C÷ÉŊé§?Øxōâ~€;wĘš1[oäü’ zžĖĀ­DEį™Ą3ؓIĀ~]Rlë‡ÂĐ~%yēŨZ+1Ĩ“‘ˇS5€ISZjŨˆVŨF˜H.íâ~Øx˛Vēvî: ÁÁß ūNÂ]īÁĩm°ņ¤ôŊxā⁍/4į3}÷Ȉšå ŲÁÆ €īË ŧcá÷ŧįÃÜËŅ'ŠŨ› ālŽ/ØIbˇ×*aSƒ1š€Qų&1Ŋ%?3jŨˆVŖŨDÂ\9ũß­–ŽŸ…;wÂRē'Å3¸Ŋō‹jĐ=K3ŌįúY ž™¸ōŌ•—ú„xį‚ {vi ›•ø• Đ1ģēĀ\[¸ûĸSÎĻxĀ>‘lĪÕ´ŠOĻcJF Ū’Ÿ™4Ũu­WĪÁúGjĨ…OĄī9šhn…Ė$Ä8‰ņ3ŌÖÜ*}ę}ō˧RúōÎÁÕwŽžŗ~;@4îîßķžk’7s´yĀī‘v|´úīaāŽE%÷ačLÄí> úļĸÉô ˇj0&ŋ3šÂäg’ĻÜØëÆĢĨÚôŽËČŊ_BöAq‘Ū“Œ€=v’’´…¯Aļœ&fįūolnūũúũĄõonų4–ŲÖ|ˇ6j™dļų.dļ‰nÆÆdoĶūGq˙đ?eØėíÛĩķIENDŽB`‚libspng-0.7.4/tests/images/bgbn4a08.png000066400000000000000000000002141442617636000176100ustar00rootroot00000000000000‰PNG  IHDR Ųs˛gAMA† 1č–_bKGDĒ#25IDATxœcüĪĀĀ…œh41"œ,ß(,?†žŖa0T1`4GÀ*ŒâhPÃß* }Äë…ČIENDŽB`‚libspng-0.7.4/tests/images/bggn4a16.png000066400000000000000000000042541442617636000176240ustar00rootroot00000000000000‰PNG  IHDR ‰ãn<gAMA† 1č–_bKGDĢ„ Ģ)UIDATxœÅ—_h[ᯖ¤ŖcëøHuRGTŽ(8…ēBē-ąŊŽPŌ.%4ģČäĐBiKa‰w‹]Hž˜“‹ĐôĸɐX°ƒf%…$όŌz„Ōâ•mĄięJ­âWG‘ícũ9–wņ~Ąt×5™OĮŌû>īķ>Īķ`eaX‡=§`ԂÎÂą8ūGČÁŲ?ÃÅ]đŪeøč øį_áî^Xŋ.ŋw÷ĘŲGOČ3wÉ˙d‡ä3Ž ČgŽZōÃē|'€feĄf[šŽđĄģZģ!pô&„Ū†æuhÁĶ`c:Īoȇt’ræiĐ^g›)XŸ÷Ŧ]‚Uꨧ V€Z Ŧl-Ķ5ŦC­`åœT$uá!Z˙?¯?pđ߅ž÷úCgęĩj@dÆą­t-ŖYYéŧ˅Ēae}'Ą;ūĶȀ~BčÉ@3í x'Ą“ÍŒB ^ÚSĐĖA#nÖr°2õÔrāä :QˇfGR`fA‹dvŸ ž“°<Éúæ@˙— _„Đ"ôŒAÃƒÖ‡ā†Ã°ųˆ°qXÎZĘ3îS°ö:Ŧ܀{ĮĀŲՃ°< ũ';âBß ˜1Å­Ú/¤sß,ÍZYßŖ íƒ` ôŊ`pgĄ=ũ°ųUĀ<´¯@#ëŋƒÕÔ× ö/¨ļ`ų6,ÍÂÖҚ̀Uķ]čŊ@`7ø&°kcā{ ‘Ŧī ø?€ā %Ÿ€õ:4ßo6¯Jۈœ­ûaõÔKāŧËGaɄÅ ¤ģ?‘ ˜¯@¸=ķ Ā!”eæū/Ĩsߨ˜VV‹CpŒA‚5AhīÎ Đ.AŖk%¨˙œ,—ā?%¨”!V¯Ų[?ƒhúv@ø[č™ã/ Ŋ ú!œ~Q`÷ZJåHVĶ@ץˇ, \Z-čt¤€VKÎęu¨Vai *(•a0îØץŦ „ =i0ŠšV„Ū†Đ.a{hQf\‘Î5 î|eeũûÁ8 Öl9QčüF häaeĒXĖC9wnĀöáš‹A˙ DŌ`ÎÉ6_B¨Áûh^ãSyŗgLg”v]˙~¸•dƒA_ ļlwŧSR€; ÕI¸[ƒ…áVF&;ū5lŨ ‘:˜ĐÛFô$g 8ŽhĄ—=oxÂöđ ™yo¯t| æ'­ŦQ†ū2 Û,ˆĻÁ4Ą§Ą4čcø9øŸ4O!°1×Dáŧ͞į͡„íŽ+3߲M:$āęm+ß?=*|~Žž5{Wb§ zÂC` ÁWÁ´ã Ũ„îčžĨč< Cäu㰈Œˇ"ĢÖj áÜQ}8ņũpî|$;>.œ;/ŋäØ—a  Ö4ŗ /@ ūĐŌĐŊ[‰Ũ/Eū}ī)x6“ĸ훏ˆÂm^•=īt„íŪ)™y#!Ã‹GÄT/ŊSŗˇo‡ž  ūS M€īOā큝]ĐÕ]#Đõ7č*gĀĮüŖÕlņķŽ!ŽļqX´Ũ…kĩdĪŨQaûâ5™ųšķŌ9Ā‹Gāå—āņ2 ėë„Á5´ŧÁe5‚$ølĐûæ ¯(–„„+ŗ˛į•¸°ũęû2ķíÛĨ‹—_’‚$`Wb“Í?@Â0 Š„9EÂį {Bāk8ĨÖ0/Æ˛VRōZ‘ųâYĩ‰šũxYfŌų\Ŋ Nvūę{k¸ ByĐ_ƒ@S­aJ­ĄcKŒj…T˜đ”ĨžcŠVE^>‘Ų[vė]Šíú7RĀĀ>é܉Ã\Üi¸÷ @ī)Ũ]SB”˙B yšŸĒ$ķ”ōķ’¸ÚŌ’hû­ŧ(ÜÎ'eĪ­ia;ČĖc“Ōš; ķ“Đœ÷kX]…HĖôžÆM%ÅI%Ŏ­äīUŒz]„ķ‚XjĨ"Æ22áØCO+hī É´ )˜•™oŗ¤ķæ„܆F\Üŧ2Ŗ7•Ĩ h+Ö'Á Š wC’ĖōQņķRY\-ūĩ‚4-äŌdĪAØ’÷.Hįía¸ķx4ÃĐ,@Ŗᜲã$„îo{ÖĘ ī“ĩdJ˜Œ;v,&Ž Š CÖ˧T$3Ķ”gVWĨsĪ“<Úyh ąßB# FP!°v VŪ”ôęl‘ ˇX$ķp]ųy]\­§.Úî_…Ųs=,l7GdæŽ){&TLč¤ÁK(į}Z)hÍ+V ¨Ģ%@¤{ëg’d"iåįiqĩāĢĸí]ŠZ^ö<”ļ›™yŗ wŌŌPgŧ+ĐŽB; ­įõ Üs%ˇ/Ģôڟ gU„<Ŋ=ĘĪĮDPēw‹ļƒüí?({ē!l7įdæÍ€tŪ•Æ:9Øø-xIđb z œš4ôŸtėhFŌkß•á2*É<#~ŽWëQ\… 4eĪ›ÂöpNfŪ.Jį,OAĮ†N 6løņ¯fŽ-ÅȌcGRęÆōŽĘí3*Ŋ%Ê’dēÕEļĢ(ø’ĸíū”(\0){n$…í­”ĖÜKJį›3ā¤`3Ģ8`ek+-w53&7–žyÉíĄiI¯Áq•áŋŧŦ4u‹-⋋=wTK°\é*Ŧėí< ĐĖeę7 ņÆÂMŪ@xÎÛŪļ"õ& ŋnܛ9åfŗ›5ęų|>ŊåäIEŊtSqEŅU´vĒvčhÆĢëķ Ŋ^R\TŦŽâÕjžˇV‚z=—XŠtšodöũū€ÕɎa€pĖˆ~î~ špÛ}€c˜‰´Æ ę׍{3§ÜlvŗæB}ŨÛëŪ†|9:§„^TčĘyJĐÖû‰zõđ5%ūW‚õįkÚīF‹øëÆĢ%X<Ņs`yšĢ  YÉ‚Eŋ`uĩŖ < šs7DOģ¯ÄNģl] #aL$7ÍøuãŪĖé4›ŨŦšP_ŋ}ũvč-‡sjõŌĩ˙ÔˇÜâqšM%Žq~áSÁĢīâWĪ­¤V‚ÚHī8ĀRĩ;ĐhdŗÁQ˙ĀęCįĸ‡Ũ_D—Ü{ĸSî>€¸Įų ųgûoÛ|€­§Øh7#aL$7ÍøuãŪĖ)7›ŨŦšPī?ÔōåϤĒ*>ˇWÍU4^üúYÁ+/âWŽ÷ˇZ‚…Oûî¨?—{ 1“ß+^ |Á{ šwâß ƒä ûu€ø'Â0™°oŲûh5ÚÍHÉM@3~Ũ¸7sĘÍf7k.Ô7žŧņeč-/NKë?ĻæRÂ×îŧxĀŋ¸ãÉZ ޟŊs'@Ŋ/ˇĐøeöA€āY˙@Ę ĸŠ[ˆ?pˆŋė| œˇHÆėQ€tŗu yŲŪßfKĪŧQë-ŅĒÚÍHÉM@3~Ũ¸7sĘÍf7k.ԇ††† _ž|IīV§?4ħ§‡îĢ–āځģN,ē'š[3“ákō†¨ęæâXf|ß>Ŧŗo$_ĩNYÃé%a”n˛æŌ Ģ}đMEûF­Ņj´›‘0&’›€füēqo攛ÍnÖ\¨ī~|÷ãĐ[~|!~æÍŨģj%¨Vķy€āĮūN€¨&#ā“HFėq€ôŦõ@šĘ ԊUā;2ÃtŪ`Š@úĒ2zKū”oļųTÛĩō3=-ĩn´–j7#aL$7ÍøõVž¨§ÜlvŗækÔe„ŨģÎŧ ĪK˙@pR (#8§âgėrR°KĪX–4‰ŠÉLšĸ;ˆVŖŨŒ„1‘Ü4ã׍{3§\6û™7a÷ŽZi÷.ųæ­3oí~`čžéi€ģŪģļ  {pi 3ŲÜ ā=)opĢQĀqdv%)Ø3É{>°ĻŌ€5–ŽXaęXiûHŒ4Ҍŧ•˜šüˤ+ĒÖ[ĸÕh7#aL$׀fü珎éiē¯ZĒV!Ÿ—_ĩ C÷ÉŊé§?Øxōâ~€;wĘš1[oäü’ zžĖĀ­DEį™Ą3ؓIĀ~]Rlë‡ÂĐ~%yēŨZ+1Ĩ“‘ˇS5€ISZjŨˆVŨF˜H.íâ~Øx˛Vēvî: ÁÁß ūNÂ]īÁĩm°ņ¤ôŊxā⁍/4į3}÷Ȉšå ŲÁÆ €īË ŧcá÷ŧįÃÜËŅ'ŠŨ› ālŽ/ØIbˇ×*aSƒ1š€Qų&1Ŋ%?3jŨˆVŖŨDÂ\9ũß­–ŽŸ…;wÂRē'Å3¸Ŋō‹jĐ=K3ŌįúY ž™¸ōŌ•—ú„xį‚ {vi ›•ø• Đ1ģēĀ\[¸ûĸSÎĻxĀ>‘lĪÕ´ŠOĻcJF Ū’Ÿ™4Ũu­WĪÁúGjĨ…OĄī9šhn…Ė$Ä8‰ņ3ŌÖÜ*}ę}ō˧RúōÎÁÕwŽžŗ~;@4îîßķžk’7s´yĀī‘v|´úīaāŽE%÷ačLÄí> úļĸÉô ˇj0&ŋ3šÂäg’ĻÜØëÆĢĨÚôŽËČŊ_BöAq‘Ū“Œ€=v’’´…¯Aļœ&fįūolnūũúũĄõonų4–ŲÖ|ˇ6j™dļų.dļ‰nÆÆdoĶūGq˙đ?eØėíÛĩķIENDŽB`‚libspng-0.7.4/tests/images/ccwn2c08.png000066400000000000000000000027521442617636000176430ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA† 1č–_ cHRMz&€„ú€ču0ę`:˜pœēQ<uIDATxœĩÖO¨gĮņīûž3gæĖœsfnb“æÖrąĨÚŌBiëFS0‹˜´6‘"…ŽŦØĻwŖA¤ŨŲE7qáĻ.Dq)ÁÖR[P¨P1¤ŪrķįÜäū9˙æ˙™y'×kJ’ÆH^^f3đû<ĪÃËĖĢD„;šôMœŊø ˙üš}û#ũņÃėxž=_ã+s€ēđö8ęRr˰æōÃĖũŒ#O°ûŽ3ĸ#ŧ÷o…!A‡°KØ!ŧĀšEö˙–×ûup‚KOōFb=Ļ…0ĒY1\ē›Õíd{9øŋōčÜf¯ōŽŌž•ßŖ3Û1AL|Ė~ÃĶŖÛŽąô}Ÿv@Ōîv {t{t#:aD'"râԌo¸æŊ·>í6*@…Hˆõp4RĶņčÄä1Ļ‹îĄ NügžČû ÷VãŦžd<‡īŖt:XŸ–AÕ¤>˜˛‡éĄ#ˆ8ąÂķģ8z“ŗ~ đ N{øēnŖCTĐ8¨š^@7fĄ{¨0>´Í÷ĩųé§cĻŋãŌ&`Ú[M4.Ļ!éE4&ÚļA¯ŊūKÔgØņÚ§Į藘×Įø›MtP]Ŧ‡k):ôb$B÷`Ų†D˜ua֎ÎķšÅ›ī1pi{čŲnŖL€ę‚O Ę.Q*F搸ÁŦ kÂē0Îæą‚ĮßxŸI‹v íĄ|´iŖtŌ(Ļ=ĸÁ7b6„5aC #a$ŧų íĪķĐS×ūAļŠŪNÛEģčÚGų˜ĸ{ˆĄéG艭8ƒÍÂgéCa, …7^īũ^=´û“ĀŸI]ŨBĩĐ­-C˜ʅˆ;8Ná{øå€AĪ$Ž6$*´¸Š0ԌÆ>ãqÉŲ„"a:Á&¨“â$´2u>åëßáOŋf[Ī9Eíĸ ÁåfskĢT‰Z˜$ ĘAņ2ņ7C—¨RĻuąŌ,_‘ãįøđŦ9ĩsßũ_~ų…Gî5ÎÍūb׏î “ŸĮô‰.07Äɛĸžôm˙K9§.rŧš<ųíũß}ų‡;îŊÅÜ˙,%"oū•—㞊îÎ6ēõ˛–Ķ"˙˛,5fų‰}<ģøÜžûoŊäë@–ËĘJ…LWW/FëJ*¨yäÁ]÷Ėß^î';ˇū øŦģDŦ‰(IENDŽB`‚libspng-0.7.4/tests/images/ccwn3p08.png000066400000000000000000000030221442617636000176500ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_ cHRMz&€„ú€ču0ę`:˜pœēQ<âPLTEö°Ū‰˙uä˙Å ˙kę˙Œ5˙­˙1ŒI—€˙Öū&4˙ą4ā2ūˆ˙˙tQ˙#ÅĶČl˙Î-˙^Ĩū>bO˙w3˙Xū ”˙.c$˙ˆ˙Äs˙Ė˙§ƒ˙‰=˙ģ˙Ņæ˙––˙Ę˙;Y‰˙ŊĮ˙\z<˙ûûüä˙˙œ˙ĩHâūA(˙˙¨Ü-˙ƒ˙žIž`˙¨%˙Ņ˙{’˙KR˙Ų~™o˙āũ÷ü¸ā˙ĩ˙Ūę|œÄ˙đüüdüāâqj˙Á€ÚŨŨõú`î˙ĨĻ˙ŌÉ˙ä+˙_öp´5˙˙čÆ˙Í]˙ƒ˙g­+Üū˙=dâūũëE˙ž˙cC=˙‹ČÎíĘ˙Â˙K˙& ˙ü‚Î˙0ŒŌ˙37˙ŖcŽ˙Ŋū˙ú›ā˙˙,˙° ˙˙GŌū6˙ĮĢÎ˙īŧ˙áf˙ČÚ×úņ™˙âû˙úũûüãY˙L‹˙›˙˙ŸāūũÔ˙Đ˙qLũŲŽÉ˙˙Āž˙€ā˙J˙{AÁ˙úûü<ņũöģŪ˙pÃą˙,~˙!˙aĸ˙1ŗ˙„ ˙Ę˙Xķũ ˙]e˙ŽG˙ū >õŪí˙L¯;[úũŦ@˙˜īū]˙~T˙´Ę˙ôũ<‘˙ã§˙e˙Č9ÆOĨ˙ėđū˙)fũ ˇĒûëœ˙˛˙˝ĸ˙ö4Šē÷ū˙…ŽãæŖ˙f˙–˙oâ˙X˙㌖˙îA˙e=˙6&˙Ü^˙ū?Õ)˙ g˙Đ˙áâúūęįKT!š˙Ōi˙ũÖÂŋ˙‹kÉ˙¯˙ē˙đōK˙ ˙Į{ũņ|S˙ę˙č˙˙˙i˙C˙¨/˙†˙5^eüõ˙Ä˙Mu˙@~˙r˙s˙>Ē˙Žo˙ĸãŖŨ›˙?@˙¯3˙ļ'ķũ˙0|˙Ũ˙{Ņ˙O%?˙đ|öū˙{S ˙9Ä˙ÖO˙UĻī˙õ¯Á˙˙­>˙Û/W˙€Ļ˙˙í˙’Lb˙Ú˙ļzŽä˙įâãė¤ū Y+˙´š¸ÃÚ˙Ō˙ī˙œĐē˙+ĀJǝIDATxœc8B0­ĀËÛmĘŪ\Ü 4ģēRRޝ÷ÂĨ Ž™šĨ#åúõ´*ė \“kjZÜÜî]OÛhœ‹MÁ$Ŗ]Ũunĸëų6nŧŧ SUr„„n°Č:Ņõ›› ›ļa(`ˆîéYwôhsáâÅŅņh \‹å7OÚŋßÛ{ĘŅŖ—[›BCWĮŖ*˜UŦąy˙~ƒ‹§œ5žÜú5T]ŨEÁŖ$įÍWĻvwk^”a\ą",:ÚÂâđAd9IÎW 45ãdĒV„…UOˇ8\YšIÁ–¤˛ŗ¤$ã.lk›ļzõô•zz‹ Á $\\öîžöÉÖöÁÁczÎí‚)øÜnh4€CÁÅĘkÚ'–ƒ™™¸Ž]¸pnÕ.¨‚ĩí†OM^ŨāČąr-eš8q́¯_žŧ°jÕ­[ģ Nl)˜ąĀæs6SÆÄE^ŋ~YPtKQQŅŦāö ĪۇNŪXģĀ&ûŅļŒ]‹I¯ŧ{7(ˆ“31‘s+HÁ‹׀ îŦ]jSRˇokVÖŠe+­OŸ>áį×{b߆’ÎÎkõ'īܙŧtéÜ>šŦ†/_N-[&$T"Žâ×+ŧa)P^Ëß˙M‚eˆ#ƒÎ—S‘>BΜ93ožŠĘûŪĶr “S}}ĩü÷TT<žtIįæÍFŸūūķQQĮyyßŋWąē’áMjjmíž=ĪvîtrÚ´‰mIx8PÚ=˙ô—<°7ßėØQ[;įųs{¨ü“'bbîų ͐€J˜9ŗhŽ’’Ŋ‡77wyųüųībˇ§į!b͞ˆ‡Gé™ĒLJ‡šųōr lŦõ[”$gÉcföL,ΝËžŽ†–ĒëĖĖb´ĩUīßŋo:aûÛ#č€áČã˜˜Ųŗõ'e?ĒaHƒĶCŖcČĮ15Ã-^ņk4ĨŗÃ3IENDŽB`‚libspng-0.7.4/tests/images/cdfn2c08.png000066400000000000000000000006241442617636000176170ustar00rootroot00000000000000‰PNG  IHDR  ’އgAMA† 1č–_sBITwøĩŖ pHYs2R0“'IDATxœUŌaqė0 ❙S…P…Ŗpú ŧR8 …@h TÜvœ6“­]iWÖ[‚•dogs’ĻPzœ`qĨƒfX)ÎÁ\ØŠÔo_Õŗú_ÕĒžĢL­čNNŊÔÔÂqĩ uõõŨ*nę˛VõS “pv5…%/ŋsķÄÃūPdŲĶ­G"ÃGx%Í ÎØ^Ę ‘.UņtlŲ[ômvÅ­æ=ĢKú˜īđëę#ãž~ãÍō˛âe–%˙•ČĨk”ƒQō|°Üy1ļ]āŒÁ(°+AFK F898Ë%|– OŌŽĩŲISSq-rųœÚ3­˯Ä[œ"ĮÖ}p4Į=AÚB´Yiˇ9ŲQ@uüĢēÜĮ3uĻs đˇIENDŽB`‚libspng-0.7.4/tests/images/cdhn2c08.png000066400000000000000000000005301442617636000176150ustar00rootroot00000000000000‰PNG  IHDR įjøgAMA† 1č–_sBITwøĩŖ pHYsčUeëIDATxœ}’kąÃ „ŋĖÔĀZĀB,ÄB,ÄB,ÜX¨…XĀŽ…#!÷BÛt‡æŧvwēø@ÕŸ7xŨķÅēûˆG93Ŧ°–‡ŌzW¨õÚŖÁ Ŧ/ X ”Ū]Dˆđ,ŗl¸paŨrä Á)üf˜äåK™ë+ĐJ^‘?Î]OļČFŦÃČsÜaMĖ‚h$kķįčŦQt0‘Ē†œN¤;8Ātõ-øōFŅ× Bf ƒđ=SäpÔԕĸéj†Ad­ņf"ŋ(>Ā‘§/6íņƒ‡ŪŠ~kĶÍ\Ēhžû¤IENDŽB`‚libspng-0.7.4/tests/images/cdsn2c08.png000066400000000000000000000003501442617636000176300ustar00rootroot00000000000000‰PNG  IHDRKm)ÜgAMA† 1č–_sBITwøĩŖ pHYsO%ÄÖ{IDATxœEÎQ Â@DŅS‚ga-ÔB+ X JĀÂV+Ą+Ą•°|4…ų™L2šš]C0Ø¨Ėp|ĖĄb?LÜiyŨ+[Ū}‹5kÉIR0_ķč6öÛ6T$'GjDĮ>Pƒåe}üP] ˛ĘaOŨ_w‡ |*°/Đ^ÖŽIENDŽB`‚libspng-0.7.4/tests/images/cdun2c08.png000066400000000000000000000013241442617636000176340ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA† 1č–_sBITwøĩŖ pHYsččĩ{RkgIDATxœ•–á‘Ü …ŋÛqjhÁW‚¯„K ›˛%ÜĩāB q ´@ ÉFØx7šŲ zOŌâí/Ī˃č/[‚ŌĶÃŧ ĖĶ‘mū@`÷œ]ąâ‹–—]ˇ6ÅĖđy~<ķK‡rô _0›Ī5úޜ<øÎY‚>ŽŠüKīXa;eĒ" °tÅōĖą[ųˇ‚Iđ=öūd›u5úV3>™œ&?wÁVĶļÂīcš¨[ÁlĖJÎÁ§Ę6đđYՖ`íŠä÷ Ļ⤉ē)’ƒ?euSXŅ,đQOyģ’ģŠKNã]J:#< CŠL=ÜŅvĀÖ%Đ$Įƒā'ŦĖJēwÉMãk=b+\u;ĩˇzwPÎŪá^<'p‰ųbÔÆSļąë d’ų(@J,ßú=ž9Öŧ¨‹ūnHÜ]¯Ģ)ŌõrtÆå`o´‘I­ŽąAŲtėÕUÉäۛDČųĄVŌbmŌB5û˙‚‡•¤č(Y¯‡k€TnģŌs=xBdĢ÷"û\`ÅÕÜ"[ųđ fRhz0'Ė×meÍhCMŨ¨ķĻ™×Â3ģĢÂß … āģí ×LdŲ¯Ō?`ą~+÷Іdl€Ž*#ßųîp]īq2ÅÃ^ÉŠ8ą÷ũ˛Ē|Î_ rj˜Ŋ:2CŋkiĮʙ9Ũfˇfč |õŅÖAĐō¯áD—īũĩĪJi?žŽßcÖ~íđđ’+Œ+ ᝄÁÛt>ŠrlŅČæ5ö‰yM.Ņåķf P­6Āņųū‚ũ@×Î.lģIENDŽB`‚libspng-0.7.4/tests/images/ch1n3p04.png000066400000000000000000000004021442617636000175420ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMA† 1č–_sBITwøĩŖ-PLTE"˙˙˙ˆ˙"˙™˙˙fŨ˙w˙˙˙™Ũ˙˙ģ˙ģD˙˙DŌ°IŊhIST@p0`` P€@0PpH™YAGIDATxœcčč =sfÕĒōrcãwīfÎd C•+(HŽ*×ŅTîŨģä qČ@å*)‘#€ĘMK#G•ģ{7āÄęŅ‚}IENDŽB`‚libspng-0.7.4/tests/images/ch2n3p08.png000066400000000000000000000034221442617636000175540ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_PLTE"Dõ˙íw˙wË˙˙ :w""˙˙˙""˙ŦUf˙f˙ff˙˙"Ü˙˙Ė˙™DD˙UU"ËË˙DDU˙UËË3˙ėÜí˙˙ä˙Ë˙ÜÜD˙Dff˙3D"íí˙ff˙¤D˙˙ĒííËËū˙˙ũ˙ū˙˙3˙3U*˙ˆˆ˙ĒĒDˆˆ˙äËē["˙"f2˙˙™ĒĒ˙UĒĒËc˙Ô˙Ēw:˙DDÜkf˙ˆBė˙ÜkÜ˙Üē33íís˙˙ˆ™J˙˙w˙ƒ˙ēēū{˙ū˙Ë˙™™"˙˙ˆ˙˙wˆˆ˙Ü˙3Ē3˙˙™™2f˙ē˙D˙˙˙Ē˙wūūĒJ™˙˙f˙""™‹˙U˙˙˙˙ˆ˙U˙˙ū˙ũū¤˙Df˙˙˙f˙3˙˙U˙wwˆ˙D˙w˙˙ff˙˙í˙õí˙˙˙D˙"˙˙ííˆ˙˙w“˙"ÜÜ33˙ūūēē˙™˙˙33cËēēŦ˙U˙˙Ü˙˙3{ūííUU˙Ē˙˙ÜÜ˙UUfÜÜÜ܃˙ww˙ūū˙˙˙Ë˙UUwwūūËËū"˙˙"DD›˙3˙ÔĒU™™˙™™ēē*U˙ËË´˙f˙›3˙˙ēĒĒBˆSĒ˙ĒĒíēē˙˙ūD™™™™˙Ė™ēˆˆÜ˙“"Üū˙ūĒSwwË3˙í˙ē˙33í˙í˙Ĉŧ˙wĒff""Ü˙Ë˙Ü˙Ü˙‹ËUUˆ"˙˙Ë˙Ëí˙ˆˆDD[ē˙ŧw˙™˙fē˙ēwwsíū33ē˙w˙DĒ˙Ē˙ūū""Ä˙ˆíí™˙™˙U˙"˙´f ˙Ü˙ēē˙˙ˆ˙ˆ˙3˙šŽĶbhISTMĖõãąIDATxœ ÁĀG¨ŒHd+;3 ekXegīŠĐ°÷**4h˜ ĢlĘŪŖŒ†•Y2˛÷žģ˜Â÷æWú˛—ĻÜ%äsƒžŧÅyŽiHÉCLå*;8Ā"éųˆŧKE6sŠxš'HKđ?ĶYÎâ9ōķsčÄŖüČ>ĸ<ÅČN3ūĄ7_˛x•2L&1­8ÁP~!^ãŠđûŠGwÖM¨Ę,ÚԘ´g%҆<ĪĪlŖ YHŦĻ(#9ĘV–pšŲÄ&Jō8S‹ÜüŎÄNĘņ/ą–÷YĪ×Ä!*“‹˙¨Ä%ķq’|ç,Ŗ5ß1œx“j<Å8*p¤ n˛‡Ĩt ¯ĐŸ¤D;Ö1a1ˆ~ô!ŋ“‘'ŲBœŖ ÉËō0šÜū‡–ČāIENDŽB`‚libspng-0.7.4/tests/images/cm0n0g04.png000066400000000000000000000004441442617636000175400ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_tIMEĐ "8Ũœ˙€ČIDATxœ]ŅÁ Â0 P*@đĄ#° #TâČ10lPF`Ø F=•ŸÄIQâ*įÅuí”`%qk HžņšˆŠņ´€m÷Íüĩāߟ Ņ=,¸fėOK į ĐtŽĀ(Čīä’×ĻíF ;čP瀝ž{xpį]9‡/p*$(ė*éyėՃ ×ūÚéįč@÷Cŧ  cÔqž‹NÛU#„)11ˇ.räđfä0°ägh(ĨtũŲÂEø˙‰kIENDŽB`‚libspng-0.7.4/tests/images/cm7n0g04.png000066400000000000000000000004441442617636000175470ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_tIME˛ V ČIDATxœ]ŅÁ Â0 P*@đĄ#° #TâČ10lPF`Ø F=•ŸÄIQâ*įÅuí”`%qk HžņšˆŠņ´€m÷Íüĩāߟ Ņ=,¸fėOK į ĐtŽĀ(Čīä’×ĻíF ;čP瀝ž{xpį]9‡/p*$(ė*éyėՃ ×ūÚéįč@÷Cŧ  cÔqž‹NÛU#„)11ˇ.räđfä0°ägh(ĨtũŲÂEø˙‰kIENDŽB`‚libspng-0.7.4/tests/images/cm9n0g04.png000066400000000000000000000004441442617636000175510ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_tIMEĪ ;;u0āČIDATxœ]ŅÁ Â0 P*@đĄ#° #TâČ10lPF`Ø F=•ŸÄIQâ*įÅuí”`%qk HžņšˆŠņ´€m÷Íüĩāߟ Ņ=,¸fėOK į ĐtŽĀ(Čīä’×ĻíF ;čP瀝ž{xpį]9‡/p*$(ė*éyėՃ ×ūÚéįč@÷Cŧ  cÔqž‹NÛU#„)11ˇ.räđfä0°ägh(ĨtũŲÂEø˙‰kIENDŽB`‚libspng-0.7.4/tests/images/cs3n2c16.png000066400000000000000000000003261442617636000175510ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_sBIT Đ7Á~IDATxœí—Á Ä@ éĘHú¯*)ã^¤–­ <øÔ Ö4\uˆ wŠClˆPgXŅĀPR3Ū› ßŸ:Ž~Ŧį! N°$ # ĨĀ|€÷GĐā}L„ˆCliāč(đūŧ?‚īc"B@aEDG@É0S&i\vgIENDŽB`‚libspng-0.7.4/tests/images/cs3n3p08.png000066400000000000000000000004031442617636000175640ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_sBITŖ’ BTPLTE’˙˙’˙˙˙Û˙˙m˙ļm˙ļ˙˙’Û˙I˙˙$˙$˙I˙˙Û˙I˙ļ˙˙˙$ļ˙˙ے˙˙m$˙˙Im˙ßp+KIDATxœ…ĘĮ‚ąŗbWT°íŋ§#$īt†NĐzC{h ÍĐĐĸt….Đ žĐm t‡nĐÖĐŊ :Bø"@3áwôŽ&IENDŽB`‚libspng-0.7.4/tests/images/cs5n2c08.png000066400000000000000000000002721442617636000175540ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA† 1č–_sBIT&ŪCbIDATxœí•ą€@ Ô;/AûÅwŒņl@*QEDįÂqm\Âa .[pÚ=ÁjËEČãŪ'ˇ-Xļ@O0‚ŽÂîō ųWyūH0‚o y‚<AūaŲ=Á^åÎošhIENDŽB`‚libspng-0.7.4/tests/images/cs5n3p08.png000066400000000000000000000004171442617636000175730ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_sBIT&ŪC`PLTE˙B˙Å˙˙{˙Ŋ˙˙˙Å˙Ĩ˙˙ŪĨ˙c˙˙œ!˙˙Z˙:˙:c˙æ˙˙˙Z!˙˙œ˙Ūæ˙˙˙„˙˙ŊB˙˙{„˙˙G/…õKIDATxœ…ÁĀ€0îîîũ[>“@õ‚VđjÁ%(‡`„‚Rđ A XŠāt‚Y VA,؉`d‚\p *Á-hÄÆq>íÖņģIENDŽB`‚libspng-0.7.4/tests/images/cs8n2c08.png000066400000000000000000000002251442617636000175550ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA† 1č–_LIDATxœíÕA 0 BQšācķ¯jSŌ‰hūi  īDŠkŅĀ`˜j¸žė}ų0M‰îāA°ā_ā ‚‚uæëÜoFĄäĒIENDŽB`‚libspng-0.7.4/tests/images/cs8n3p08.png000066400000000000000000000004001442617636000175660ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_`PLTE ˙˙˙˙ß ˙˙?˙˙˙€˙˙_˙`˙˙ā˙˙Ÿā˙@˙˙˙Ā˙˙ß ˙˙ŋ€˙Ā˙˙ŋ˙Ÿ`˙˙@˙˙_ ˙˙?ŗŗcKIDATxœ…ÁĀ€0îîîũ[>ĩ |‚Gp Á"˜ƒ l‚X *A/P™ $‚BP ZÁ.ˆŠ Œ‚Y° NÁ-xÅ)â>øķSŲIENDŽB`‚libspng-0.7.4/tests/images/ct0n0g04.png000066400000000000000000000004211442617636000175420ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_ČIDATxœ]ŅÁ Â0 P*@đĄ#° #TâČ10lPF`Ø F=•ŸÄIQâ*įÅuí”`%qk HžņšˆŠņ´€m÷Íüĩāߟ Ņ=,¸fėOK į ĐtŽĀ(Čīä’×ĻíF ;čP瀝ž{xpį]9‡/p*$(ė*éyėՃ ×ūÚéįč@÷Cŧ  cÔqž‹NÛU#„)11ˇ.räđfä0°ägh(ĨtũŲÂEø˙‰kIENDŽB`‚libspng-0.7.4/tests/images/ct1n0g04.png000066400000000000000000000014301442617636000175440ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_tEXtTitlePngSuiteOUĪL1tEXtAuthorWillem A.J. van Schaik (willem@schaik.com)ŽĖG8tEXtCopyrightCopyright Willem van Schaik, Singapore 1995-96„P8ûtEXtDescriptionA compilation of a set of images created to test the various color-types of the PNG format. Included are black&white, color, paletted, with alpha channel, with transparency formats. All bit-depths allowed according to the spec are present.M k9tEXtSoftwareCreated on a NeXTstation color using "pnmtopng".jdytEXtDisclaimerFreeware._€,JČIDATxœ]ŅÁ Â0 P*@đĄ#° #TâČ10lPF`Ø F=•ŸÄIQâ*įÅuí”`%qk HžņšˆŠņ´€m÷Íüĩāߟ Ņ=,¸fėOK į ĐtŽĀ(Čīä’×ĻíF ;čP瀝ž{xpį]9‡/p*$(ė*éyėՃ ×ūÚéįč@÷Cŧ  cÔqž‹NÛU#„)11ˇ.räđfä0°ägh(ĨtũŲÂEø˙‰kIENDŽB`‚libspng-0.7.4/tests/images/cten0g04.png000066400000000000000000000013461442617636000176360ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_iTXtTitleenTitlePngSuiteÕŦÅ8iTXtAuthorenAuthorWillem van Schaik (willem@schaik.com)EW ¤AiTXtCopyrightenCopyrightCopyright Willem van Schaik, Canada 2011Ōë3Á iTXtDescriptionenDescriptionA compilation of a set of images created to test the various color-types of the PNG format. Included are black&white, color, paletted, with alpha channel, with transparency formats. All bit-depths allowed according to the spec are present.~5 DGiTXtSoftwareenSoftwareCreated on a NeXTstation color using "pnmtopng".Ä$iTXtDisclaimerenDisclaimerFreeware.Ķž2 LIDAT(‘cøÆÆ..ĄĄiiåå d ()!¸ä ü˙āΜIžÜI@–@ \ĩŠ<„“Ā\˛ÜŨģΜ!OÁ…zŽtŨŗķQUÖ4—IENDŽB`‚libspng-0.7.4/tests/images/ctfn0g04.png000066400000000000000000000013141442617636000176320ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_iTXtTitlefiOtsikkoPngSuiteķČI9iTXtAuthorfiTekijäWillem van Schaik (willem@schaik.com)M˛˜ÕHiTXtCopyrightfiTekijänoikeudetCopyright Willem van Schaik, Kanada 2011ũŋ‡ëiTXtDescriptionfiKuvauskokoelma joukon kuvia luotu testata eri väri-tyyppisiä PNG-muodossa. Mukana on mustavalkoinen, väri, paletted, alpha-kanava, avoimuuden muodossa. Kaikki bit-syvyydessä mukaan sallittua spec on ​​läsnä.Į6qY?iTXtSoftwarefiOhjelmistotLuotu NeXTstation väriä "pnmtopng".QmĨ]-iTXtDisclaimerfiVastuuvapauslausekeFreeware.üw/HIDAT(‘cøÆÆ..ĄĄiiåå d ()!¸ä ü˙āΜIž —<wÕ*ōиd ¸ģwŸ9Cž‚ˤ ųrōAĐÄŊIENDŽB`‚libspng-0.7.4/tests/images/ctgn0g04.png000066400000000000000000000022361442617636000176370ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_ iTXtTitleelÎ¤Î¯Ī„ÎģÎŋĪ‚PngSuite ‡@šFiTXtAuthorelÎŖĪ…ÎŗÎŗĪÎąĪ†Î­ÎąĪ‚Willem van Schaik (willem@schaik.com)Ö*6f‰iTXtCopyrightelΠÎŊÎĩĪ…ÎŧÎąĪ„ÎšÎēÎŦ δΚÎēÎąÎšĪŽÎŧÎąĪ„ÎąÎ ÎŊÎĩĪ…ÎŧÎąĪ„ÎšÎēÎŦ δΚÎēÎąÎšĪŽÎŧÎąĪ„Îą Schaik van Willem, ΚαÎŊιδÎŦĪ‚ 2011×#ävõiTXtDescriptionelΠÎĩĪÎšÎŗĪÎąĪ†ÎŽÎœÎšÎą ĪƒĪ…ÎģÎģÎŋÎŗÎŽ ÎąĪ€ĪŒ έÎŊÎą ĪƒĪÎŊÎŋÎģÎŋ ÎĩΚÎēΌÎŊΉÎŊ Ī€ÎŋĪ… δΡÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎąÎŊ ÎŗÎšÎą Ī„Îˇ δÎŋÎēΚÎŧÎŽ ΄ΉÎŊ Î´ÎšÎąĪ†ĪŒĪĪ‰ÎŊ ·΁ΉÎŧÎŦ΄ΉÎŊ-Ī„ĪĪ€Ī‰ÎŊ Ī„ÎŋĪ… ÎŧÎŋĪĪ†ÎŽ PNG. ΠÎĩĪÎšÎģÎąÎŧβÎŦÎŊÎŋÎŊĪ„ÎąÎš ÎŋΚ ÎąĪƒĪ€ĪĪŒÎŧÎąĪ…ĪÎĩĪ‚, Ī‡ĪĪŽÎŧÎą, paletted, ÎŧÎĩ ÎŦÎģĪ†Îą ÎēÎąÎŊÎŦÎģΚ, ÎŧÎĩ ÎŧÎŋĪĪ†Î­Ī‚ Ī„ÎˇĪ‚ Î´ÎšÎąĪ†ÎŦÎŊÎĩÎšÎąĪ‚. ΌÎģÎŋΚ ÎģÎ¯ÎŗÎŋ-βÎŦθΡ ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎĩĪ„ÎąÎš ĪƒĪÎŧΆΉÎŊÎą ÎŧÎĩ Ī„Îŋ spec ÎĩίÎŊιΚ Ī€ÎąĪĪŒÎŊĪ„ÎĩĪ‚.ψ)‰iTXtSoftwareelΛÎŋÎŗÎšĪƒÎŧΚÎēĪŒÎ”ÎˇÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ ΃Îĩ έÎŊÎą Ī‡ĪĪŽÎŧÎą NeXTstation Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšĪŽÎŊĪ„ÎąĪ‚ "pnmtopng".CxËëCiTXtDisclaimerelÎ‘Ī€ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇÎ”Ī‰ĪÎĩÎŦÎŊ ÎģÎŋÎŗÎšĪƒÎŧΚÎēΌ.,ž¯l]IDAT(‘ÎA! CŅhÁ°€,`Ą°P X@ ˛ĶŨé=Į˙™ KŠĩĩŪĮ˜D’šiƒfkiĐų}„ģ‘ä{) ä%÷Ŋ5ČÎŅ #īāE9ōA˛“EIENDŽB`‚libspng-0.7.4/tests/images/cthn0g04.png000066400000000000000000000023651442617636000176430ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_&iTXtTitlehiā¤ļāĨ€ā¤°āĨā¤ˇā¤•PngSuiteSũKˇ>iTXtAuthorhi⤞āĨ‡ā¤–⤕Willem van Schaik (willem@schaik.com)ĪM}įhiTXtCopyrighthi⤕āĨ‰ā¤ĒāĨ€ā¤°ā¤žā¤‡ā¤Ÿā¤•āĨ‰ā¤ĒāĨ€ā¤°ā¤žā¤‡ā¤Ÿ Willem van Schaik, 2011 ā¤•ā¤¨ā¤žā¤Ąā¤žßՅeiTXtDescriptionhiā¤ĩā¤ŋā¤ĩ⤰⤪⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ PNG ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ē ⤕āĨ‡ ā¤ĩā¤ŋ⤭ā¤ŋ⤍āĨā¤¨ ⤰⤂⤗ ā¤ĒāĨā¤°ā¤•ā¤žā¤° ā¤Ē⤰āĨ€ā¤•āĨā¤ˇā¤Ŗ ā¤Ŧā¤¨ā¤žā¤¯ā¤ž ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ā¤ā¤• ⤏āĨ‡ā¤Ÿ ā¤•ā¤ž ā¤ā¤• ⤏⤂⤕⤞⤍. ā¤ļā¤žā¤Žā¤ŋ⤞ ā¤•ā¤žā¤˛āĨ‡ ⤔⤰ ⤏ā¤ĢāĨ‡ā¤Ļ, ⤰⤂⤗, ā¤ĒāĨˆā¤˛āĨ‡ā¤ŸāĨ‡ā¤Ą ā¤šāĨˆā¤‚, ⤅⤞āĨā¤Ģā¤ž ⤚āĨˆā¤¨ā¤˛ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤Ēā¤žā¤°ā¤Ļ⤰āĨā¤ļā¤ŋā¤¤ā¤ž ⤏āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ‹ā¤‚ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ. ⤏⤭āĨ€ ā¤Ŧā¤ŋ⤟ ā¤—ā¤šā¤°ā¤žā¤ˆ ⤕⤞āĨā¤Ēā¤¨ā¤ž ⤕āĨ‡ ⤅⤍āĨā¤¸ā¤žā¤° ⤕āĨ€ ⤅⤍āĨā¤Žā¤¤ā¤ŋ ā¤ĻāĨ€ ā¤ŽāĨŒā¤œāĨ‚ā¤Ļ ā¤šāĨˆā¤‚.úԑ‘iTXtSoftwarehi⤏āĨ‰ā¤ĢāĨā¤Ÿā¤ĩāĨ‡ā¤¯ā¤°ā¤ā¤• NeXTstation "pnmtopng 'ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰ ⤰⤂⤗ ā¤Ē⤰ ā¤Ŧā¤¨ā¤žā¤¯ā¤ž ā¤—ā¤¯ā¤ž.ÅQ×BiTXtDisclaimerhi⤅⤏āĨā¤ĩāĨ€ā¤•⤰⤪ā¤ĢāĨā¤°āĨ€ā¤ĩāĨ‡ā¤¯ā¤°.-O@ģ`IDAT(‘cø˙_Pđ˙cc—ĐĐ´´ōr˛””ū˙‡q;:ČwæLō\\ū˙;‰Če O 4ô˙°“V­"OÉI@†Œ 2đĖō`ÜŨģîŪ%K@HķŲ ÍûÁIENDŽB`‚libspng-0.7.4/tests/images/ctjn0g04.png000066400000000000000000000016551442617636000176460ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_ iTXtTitlejaã‚ŋイトãƒĢPngSuite\8iTXtAuthorja著者Willem van Schaik (willem@schaik.com)åņĖĄSiTXtCopyrightjaæœŦæ–‡ã¸č‘—äŊœæ¨Šã‚Ļã‚ŖãƒŦãƒ ãƒ´ã‚Ąãƒŗã‚ˇãƒŖã‚¤ã‚¯ã€ã‚Ģナダ2011„_mžwiTXtDescriptionjaæĻ‚čρPNGåŊĸåŧãŽæ§˜ã€…ãĒč‰˛ãŽį¨ŽéĄžã‚’ãƒ†ã‚šãƒˆã™ã‚‹ãŸã‚ãĢäŊœæˆã•ã‚ŒãŸã‚¤ãƒĄãƒŧジぎã‚ģãƒƒãƒˆãŽã‚ŗãƒŗãƒ‘ã‚¤ãƒĢ。åĢぞれãĻいるぎは透明åēĻぎフりãƒŧマットで、ã‚ĸãƒĢãƒ•ã‚ĄãƒãƒŖãƒãƒĢを持つ、į™Ŋéģ’、ã‚Ģナãƒŧ、パãƒŦットです。すずãĻãŽãƒ“ãƒƒãƒˆæˇąåēĻが存在しãĻいるä앿§˜ãĢåž“ãŖãŸã“ã¨ãŒã§ããžã—ãŸã€‚Ã ´ciTXtSoftwarejaã‚Ŋフトã‚Ļェã‚ĸ"pnmtopng"をäŊŋį”¨ã—ãĻNeXTstation色上ãĢäŊœæˆã•れぞす。ƒø02iTXtDisclaimerja免č˛Ŧäē‹é …フãƒĒãƒŧã‚Ļェã‚ĸ。vđCæeIDAT(‘c``øÆÆ..ĄĄiiåå d *)ũ˙ãvt# ¤dlü˙?Œ;s&9 ˙˙‡Ȱj9h\rœÄ°{÷™3ä Μš{—w÷n ŖŪŊ#CNņš H–>IENDŽB`‚libspng-0.7.4/tests/images/ctzn0g04.png000066400000000000000000000013611442617636000176600ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_tEXtTitlePngSuiteOUĪL1tEXtAuthorWillem A.J. van Schaik (willem@schaik.com)ŽĖGAzTXtCopyrightxœsÎ/¨,ĘLĪ(QĪĖÉIÍU(KĖSNÎHĖĖÖQÎĖKO,Č/JU0´´4Õĩ4‹a¤RZ”ģzTXtDescriptionxœ-ŽąÂ0 D÷~ÅMLĨ˙Ā„Xŋ`\C"Ü8J U˙W°Yž{wwÛRŗ’g+°]|?ōBOéā&ä2à .ŨáI†ĩlīĐL­}Ģá "$ÜŽg<Ŧ-ä.…õ=KM†ģŋkĘ.ãQIÅ#|Ě=´&'*Eô÷ŧQé5 o˙ā>ᤊ{öã,ÕSPmŨ{˜­Íš<‡}mŦéUxoGmŌĨøôōVōbzˆ@zTXtSoftwarexœs.JM,IMQČĪSHTđK).I,Éō’ķsō‹J‹3ķŌ” ōrKō ōŌ•ôĻėBžQ¨ŽzTXtDisclaimerxœs+JM-O,JÕU`ÔŽĩīČIDATxœ]ŅÁ Â0 P*@đĄ#° #TâČ10lPF`Ø F=•ŸÄIQâ*įÅuí”`%qk HžņšˆŠņ´€m÷Íüĩāߟ Ņ=,¸fėOK į ĐtŽĀ(Čīä’×ĻíF ;čP瀝ž{xpį]9‡/p*$(ė*éyėՃ ×ūÚéįč@÷Cŧ  cÔqž‹NÛU#„)11ˇ.räđfä0°ägh(ĨtũŲÂEø˙‰kIENDŽB`‚libspng-0.7.4/tests/images/exif2c08.png000066400000000000000000000033741442617636000176450ustar00rootroot00000000000000‰PNG  IHDR üíŖŌeXIfMM*bj(‚˜r‡iŠÜHH2017 Willem van Schaik0220‘’†Ė 0100 ˙˙ASCIIPngSuite*2(:—HH˙Ø˙āJFIF˙ÛC     ˙ÛC   ˙Ā"˙Ä ˙Äĩ}!1AQa"q2‘Ą#BąÁRŅđ$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚáâãäåæįčéęņōķôõö÷øųú˙Ä ˙Äĩw!1AQaq"2B‘ĄąÁ #3RđbrŅ $4á%ņ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚâãäåæįčéęōķôõö÷øųú˙Ú ?ûąŖŅĢ=ōH΀ģŸŋŸnGôâŠ(¯æ:“nĖëĨB-jÛųŸ˙ŲēˆĒƒåIDATH‰Ŋ–ŨoAĀų¯ĮŖÆDĶø`Ô7%MJ*6ÕHĨ´PĀ+—ã€ãCB‘ĘįáĖîfšģršZĀds™ŨŊ™ßîėîĖ Øj ü'ĀÅ”Ëĸ ‡4hšĸĢiPŠ@ģ ã1XMM§ĐéĐ NṲîHĄÕ]wđÛlmĄ<™ĀlFvq°Ņ /ę^×EܨĀe4ZĢ‘ŅËKčõhHENី°1n…~_¸Ë˙ ž™)GKļ 7&áņąp‘f@0wŌäĖHŲũ?"WÚ9üí Āņ'Y’OUZoĸDō[•V}€Ŋ \$ųg›äXM( Õū€ÎŨ3¨“hAgŠ9úĖīIÆCÉĪ $wĮ$ˇģ× TZEÃH0÷ō63@DĨ‰īšã.ĸ‚Q'ŋ?Č@ˇRđ4'Î Uđv†ķ¤u Š›-iį• Ėįc+ũōKXĮ{¯Ę;'4Åũŗk,ÕđĄqG}UhYŨŽ7Āk‚ˇ šēĻoĄį˜ēĒu@} Ž+xŖŲz{“/ČZĀ}æĨ—Š›í°ˇ™åĀöžųzßÜā { cm€ũūJ‰%NXŪQ_€ŨŽå{ČÅ?ËåØÁģEŅ*M=dAé°ŗĀŗŧ)ø}§m€Īø”…GĮPÖéÜ;ĄÃįIãsŦBŗņŗeΈŗWytõ!Oædh…ŗ ;’õ>+*ä4øČđQ˛%Șļ_räŅÕ×}‚†^3×+=RūĖ”LįÜ˙#ķč'…ä é2zę&ÜMC0MģA@†…ôWgžtڇÂĩC˛Ė⋜()Ęã^[ ādS(<z^öĸˆ×˜å!s‹Ĩy5Åģ\ –%–?€_•ãįįt˜˛Žr•üˇĩ¸!,dd%č*–6@™ĘMtyå/˙‘yt-:ËSŲå+Oė2nŊēū ;Jåæ=+$IENDŽB`‚libspng-0.7.4/tests/images/f00n0g08.png000066400000000000000000000004771442617636000174600ustar00rootroot00000000000000‰PNG  IHDR V%(IDATxœ…Ō/LBQĮņ¯āsc77b XH¯ŧôš‰fŗiT#HŗŅl&‰bąX 7ƒ›››s äéũãvÎõ…sëũėžßũŨËõd:ģ™ßŪ-–Ģû‡Į§į—ס÷õĮį×fģûŪ—åcC02CCpe. ÁĀ\‚B ÂJšPd"ܖŸ’ ú" 敃s’ēTRz"Ā'õ!Ô]8šRŨ–Žđ9pg¨>čˆ7[Õmáô0EuĘŠŋûP­s"Âí'}ÁąHúˆ‚–ĸ öņ'8JEXúõiVEō?h$"ˋ?ˆCCP75Cp`,%°Ä/Ä}ĘXPĻÎļIENDŽB`‚libspng-0.7.4/tests/images/f00n2c08.png000066400000000000000000000046531442617636000174560ustar00rootroot00000000000000‰PNG  IHDR üíŖ rIDATxœu–opTåƟ÷ŪŨM6°É†° ,IŗŲ °ü–ϏhĐE‚°€@v‚ ņF‡ؖ„ŗbļ‚ k"ĸd; ą”MÂHCa´VŒˇü‰ e(R¨zCĀ H/­||ēL(8ڞOīœŋįœ÷œwžD2“ĶhÍâ\:ō9j<-ā”B>YÄY폠„‹žXÁ@WWs]-7ÔsK#ßlâÎfîiåžũ<ÔÁŖ<ŅÅîŗŧp‘_hėģNũŋŋKzNs“éLãØ,ēséɧwKĮsi…\UÄ5ķ¸ž„nŽ`cßĒæŽZîŽg[#ۛx¨™īĩRŨĪŽvwō|/eĪEöiŧywn!>Į¤-LŌ%QIæŌ4ŽČbe.WįsíxÖpC!7ņĩy|Ŗ„M wVpw[Ģš¯–ęy¤‘iĸÚĖ“­üx?ãŧĐÉË]ė9Ë//ō†Æ[ץ–ãF­Ę¤¯1ą6‰ŌXŸÅ-šlĖįļql*āŽBžSÄ=sŲVÂ} ˙XÁC•üS5߯ĨZĪ <ÕÄîfžká…v^ęāįė=Éž3ŧq‘ē†X•A]kŒ˙ƨm1éošØœÄŨiŒfą-—īæŗ}đP!;ŠøŪ\-ĄĒđxģ*yēšŨĩŒ×ķ|?mâĨf~Öžv^í`_'ŋ>ɛgxë"ĸë ą:ƒúē1žÃ¨ĩšôƒ&v&QMãą,žČeW>OãĮė.äŲ"ž›Ëķ%ŧ đb/UōÕüŧ–=õėmāĩ&ö5ķĢŪhįÍęŧ}’wÎ ōĒ!ēÕÛiP÷㇍Ú1“7QKbO¯dą7—Wķymŋ,`_!¯ņĢšüē„7~SÁ›•üg5õZŪĒįíū̉wšųm ŋkįŋ;ø}'īžD8,G"r4*ĮbU5ÄãM3ęē‘4‘Šd&ų3Ō‰Ÿų<šŒŦ$א/“udšÜEļíäōŦ ÉëÂr]DŪ•ߎödž#qÃqÍxN7öĐt…ŠŊĖü)Ŋ?žáķ7šė&+uŽšÅ—oŗî6îpûˇÜõ-[žcû÷<‚EAŠ,$¯ËĢ#ōú¨\3lU ;ã†6ÍĶŌô÷§9îūü Ÿxü”Ī˙Ë.ŗō3Žų‚/÷°Ž— ׸ŊģžbË ļ㊀ôLPz6$- KJDZ•WÅä—Tų•¸!ŦŪŌ ũ Úö2gGîᨃœ|˜÷į? _eéq–wqåiģYsŽucÃEnŋĖ]Ÿŗ19 =”Ļ…¤aÉ‘FåÅ1ųU^7ŦŌî l¤emaæŧΑorėƒöpNũ`é–æĘ?3x”5*ëN°áˇws~ņˆ"ō’+(MIî°4%"=•Ŋ1yĻ*Īß(Ŗąœ–mUĖųá6ķéÎŲF„Ĩoŗ|7Wļ2¸5Y×Á†÷š>aķ‹!а¤œ äI#’3"ŠĘccōUîytÃ4gĐōŖ9˙’˙šOWsN ũXē™å¯såvw˛f7ëÚØ€d¯0ûDŠ_ T„% RƒÂ’ŌÃRFD•2cR?([“stųw2ŗûŗé^ĀŠĪqz},Žbé–×påFˇ°f_MÔá…đAø!ˆDRRRRô>QŽ?Ŧz “ĶÚvq”›Nå”é|ŌĮYÅ\PĘÅå|q%AŽŽá:Ām‚'Ix“…Ī,ü)B E0U ĨIát)2čGw"ë9æüīĶ{„ŊnΚĘųĶšČĮ˛bV”˛˛œÁ•\äÆD āO*ŧVø ŋM(Y"`ÁlĘáá?¤KņI’V é™…ũ™aœ?‚%cX:‰K= Lį*×sũnXĘÍŋb#āL…Ë w<™đ…/[øB!ų"8F„Æ?¤Įž’Ô™R|ޤ-”ôEũÉ ŽÆĘ\=†k'ąÆÃ ĶšÉĮ׊ųÆ6-åNĀ‘g\™p…'Ū<øœÂ?Z(ãEĀ-‚õƒDÄ'ĸ ¤Øs’Z&Å+$­Ē??€2X?Œ[F°q ˇMb“‡;Ļķ÷ŗm ÷v+ƒáĖ‚kÜxFÂ;žņÂ?Y(xęA"ĸˆh…Ģ’ÔĩGÂæܝÁč0ļāģcØ>‰<<4>žWĖŖ€Í {Cāˆ+n'<.x'Â7ūiPŠ˜‡˙BkúA‰)Tņ˜'†ŗk4OMäĮv{yv6ĪÖTØÁžG6œypåÃ=7ŧøŧđΆ˛%˙ƒŽîņÃB;&ô¸D-…=ƒxÅÎŪáŧ:š×&ōKûŧŧX,°ZaŗÁn‡Ã§.Ünx<đzáķÁī‡ĸ @0(B!‹HDDŖ"Ē*âqĄiB×%2…DÚÉáähr"éIü‹lÆ R­dC–Ųä9‘īÂX7Üxŧđú0ۏbĨ, & 먋`koĮ°Wő8Žk8§‹š¯0Ŋ—C¯2īG]ãÄž„€Á„$3Ė ´ÂjC†CČv"Ī…|7\LōbŠĶü˜Š`^ a ( aEĢ#XE} [U댪-ņĐŇ4˙•éĮ9ô$ķ>â¨Ķ ÉpOÃhF˛)VXl°Ú1؁,'˛]p¸1ԃŅ^Lđa˛S•„ā™ ž aQJËŖXÃK*^‰#Ŧá-]ŧCķī™ŪÂĄ{™ˇ/! ¤{˛ 3L$[‘bƒÅĢNdē`w#Įƒá^8}ãO&đXĶB˜†/‚…Q,ŽáËãXĨĄZ!š72}‡†yo]@ ™ ›a°ĀdE˛ f;:ę„Õ… 72=ęEļ/a xDA~Ž &„ācJODáaϊšqkxNOHr9­Ū_šûÂÉ ŲƒFLv$;`vb€ 7R=H÷& 6?†(°„#„a8#ÅØ&¨pĮņ -a ˜Æä|¸Ö÷4đ/„ÂÉ ÉŲƒ'Œ.˜ÜHō$ fRü¨Ā@jÖŌÃȈ`p™1 Qa' 9:üo&üZ$“ÅIENDŽB`‚libspng-0.7.4/tests/images/f01n0g08.png000066400000000000000000000005011442617636000174450ustar00rootroot00000000000000‰PNG  IHDR V%(IDATxœ…Ō!KÄAđ÷vvvö1‚Á`1X,&‹Éf 6Á`.Ļã’`8¸"\ ‚A0ˆÁä'°ø ÷ßŲ°ģųĮž™áq’-›™Ĩ”TUŖJ !$ČÛQGÜX[đ:wÄĨuąuęuÄij ž˜įøö‚ĮŠWĀ—˙ƒGZˆ1€ŸÂÃ4ˆ)ŧų9x +1fcŧøIš¯+ąĀ$ßáÉīÂŊ8ˆlŖ)ũļÜU'0ķ÷āNtbŽ{1nG'¨Ä-qâUƒ¸)Nŧĸj7ĸī¨ÄõāÄ'ĒqMņƒ˙WĻ0‡Aü.A™Â QŪt)C[PØ l ’mAtŅDGü•Q,ƒĢ IENDŽB`‚libspng-0.7.4/tests/images/f01n2c08.png000066400000000000000000000022341442617636000174500ustar00rootroot00000000000000‰PNG  IHDR üíŖcIDATxœ­–?ė%U†Ÿ÷üũΜ3įė4"X‰ŲBc‘+ąĐ˜P°ÆF M(,hH((,Œ1ąĸ0V’€ą5ĐRĨĨŲÂBŖ %,×bîܝŲũ토“§xĪ7É}›oîst°ŒŲ™bWį¯öǘ>y€~æļų•Lkū鯝U•ÆÔ(3ĩkęÔAǃy‘Ãö=ũ›íųđôũuøeĐsŋpš’+Öȍ2Ë:ĨS†ĻAÔE7ôģįŨĶwųųúģoœyU˙~wz:`{âņ(—Ŧ'^T˜đUą*ĄqVėÄNz÷úįĀSŒØõ)đĢĶS[I[¸‹DYƒúĄ\ÁO¸*_ņßđ ?+t|' …ÄEīé3āĮ§Įļ’ˇpÕ¤$LöŧTPÁM¨ĸŠĢRÃ54ã:ŽË Ü@ƒ|í6đ­ĶÃËÛŋ|Ūa”ãŅÄ3Č @ *TTĄAƒ:ę0`pûI€vz Ęņ(žČ’‰"Š˜DÕŠŠæhbvtŠ;†ģ}ũŸ@8}ũÁ*(ģŖøfÅDv˜Ŗ8Ī䨞ꨞæižŲĢē˙âŲ÷É3ZÂŅg ”{œ žŅIķdyŠW LžδH ĖQ=~ņÂßwë'Œ¨%ßãh¤rœˆ')<æÉëG•ĀŠ‘š¨‘–h™9^zp7Æ0FÖRė(;{ ^˛xôÚš ,’#)Q%1%jĻ&ĒŅ2ÍN? pīŋĘ(Œ‰Q´T; dåâ ņČ5ĸ'ER E,’×%Î”Ė”ŠÆīßáĒGˇūˍZf; ÄvŌ×:1#i%a‰œąL1Š1oũåꂛctÆŦeØQ !ˆyÆ{b$FR:cF^ŋ›B)LĩRĢjĨ56° „1´,÷Ú €°‚xOˆÄHLÄĩÆÎ—%+XĄL”JŠL•@ØÂ<4Û ŠC„„÷¸­ÃGb"Ŧ5FĖįũΛȕRąĘN lĄÍË]B.āōø€ķøˆ„„OÄtžƒœ÷ģ'Rå(60 ĘP]ö~rpņŸÎÃgâē߅8+G° 䥲\Ė V.Ú:ņ —piĪ!„¯Â&Ō ō˛šA랝;Ø:äQD—ĐZc(ã×? „Ŗ@ØÂ&Kį‚­ƒ­ƒ€PŠÅčZâdŅåOĐįb ˛Õé31zų]贘G­Ë1(q tÚˇĨOâĀėr|1:S‚! ü?õC+Ģk[;{CƒA‰åÕE ‚ą  ‚ą ˆ‚ âéä—ŋŋĖëũIENDŽB`‚libspng-0.7.4/tests/images/f02n2c08.png000066400000000000000000000033011442617636000174450ustar00rootroot00000000000000‰PNG  IHDR üíŖˆIDATxœmÕIˆ$yĮņoD-YKWeeíÕë ˆâÁíĸxĐÆ“ ˆ ĸsčÁË@#xĐÆƒ‡đ 0"Ž ˆƒí‚ā(*"Ԉ¸ā‚=ô”ŨĩuíUŲUõŒž™ņ&eAĒjŌžŅPĻRęÂd9›”1aTIŠ)5aXL+FÃx‚xŌ\7en†hƸĸ}LĘĸôš6—2+Õ/PĘDĘa\KЌĄ”Ae@ÂÖ(ņ8ņs“¸Is ĸ†qUû˜”%К–2Wõm:Ĩ!UĶúŒQeD¨)Ã)C&5kÕ,ĩxĖÜs…Ģ[T/¸&\Ž—…K\ú{pņN°ôû`ņWÁÂ΃ų1+Õl„)ĄŽLĘŲxƄQaD݄í„Íëą­ÆÜw,;{Ëq7˛Mt ŨFƒāO|AđúzŒŖzŠ:ÔĄ-4AÔŖ‚†ŪvļļZlÄļŗâøŸŗeĮ›‘m Wƒfyãßė˙°÷ūĶ>øoûhy2ŧŪDĐR:AO+é16=‡ŪöÛMlģe›ąmÄļæėŗûÎîEEy×OlnŲBļ‘ä7ö‘ōü9DšČ#ä9FWŖ-4<ņväizövZļŗŗálÕąâz YG7Їčãž•į{č>zˆ6ŅGŊj¤ŦĻ”ÂĶ6‘ˇcĪ‘§™p°×˛˜­˜‡ÎÖŸŨüüÉā*ē†ö3%°‹îĄ%sp&aKpmœˇĪąį(Ą™pвŊ˜Ũ˜-gŽ•ČVĐ~ĻĘjvĐŨŠ)&Rz!â6ąˇSo'ŪŽ%4[ļÛ^l;Îļœm:[‹ŠČ ˛Šü,X)‡H˙lÎW#PÕTLÄ|Ûo-oÎ.)ĸ¤8iGqŅŒģ‡Žģīē{ŽģuÖHîGåíī°g6ņ[ømü~—ö.~€?Ä7i7i‡¤ÃĻ5dĨ=†Į_0?IR'iК&žĩxˇ€[Â]ęŊąĪÚ{ž r>@BtˆtČtؤf2ŌgLX2iɔĩĪX!.y—,'͑ Éđ‰ŌJqĘie<ōz<}B ,;{3:ˇrwBŠ‚n—N‡N—,'ËŅ -KQ’”XqŠpÜæØĶôzövļ[lÆlÄŦ98î;ģéųÍbEAQyN–“f¤’ŅV|JK‰'DmNĘÍ~ TÂĒcÅŲrÔŋŲCĖ(*ŖÛĄĶĨ““W†f´•vJĸ´'œļ‰ĘÍ~ TÂēcÍŲũHV‘5d +°‚ĸKŅĄÛĨ“ĶÉÉ2˛’Q$Å+^¨„ķB Tĸ+č*aų€žbts:9yF^šŌVú„Ķ’éû1{1;Ž-ĮĻc-ŌüC>§We==IENDŽB`‚libspng-0.7.4/tests/images/f03n0g08.png000066400000000000000000000006051442617636000174540ustar00rootroot00000000000000‰PNG  IHDR V%(LIDATxœ}ŌąKBQĮņīé„DQ !E‚P4.AËĄ!Š"!ƒĸHĖįÚ_BAĐ 84ĩ854 AāāŋņÔįyĘ{wšøpīš÷wÔu2ŲŨŊũÃŖ“\ūôŦx~qyu}S*—o+×­ēUM‰ €ĐۆJŨōĸ…nJ´Đ ĸ…Ž-4I´ĐŅB—Ä…FũõåųéĄvŸã‹&ũ•œĄ‹øĸû§uø0ˇč‚ „ ĀģíCį ˆÔęV”ā1øÅˆ;ā­‘ĩBgĖķųÆuš„NÛJĨ=:…ˆ'N“Œ‰{’ŅÂ1qO0">Ų1qĮÄ­v7š/ļMÜ1ĪÚ  i÷¸ø"ŪĄũ÷ûE“ž*plįCĮ°by%šļ˜ Ŧ&'€öË0ŅB{C*ú Tø L @ˆøcg\Bŧū 9IENDŽB`‚libspng-0.7.4/tests/images/f03n2c08.png000066400000000000000000000024131442617636000174510ustar00rootroot00000000000000‰PNG  IHDR üíŖŌIDATxœ}–]ˆTeĮĪŧggįĖ×ŋZû,˛ą˛Ô(ÚBˇÃB2B*M… Ŋ zxc~PwŌUwŌMW{Ņm Á"^XvabåE‚­îė|îœ}ē8į93ŗģÃa˜áŪßû˙?ĪûüQr6W°ÅŠ­ŦļĢ×Úû˛ëÖÛGŗž´7Ų§7ÛįļÚm/ڗ^Û×wډ7íîIģį-ģ÷ģī]ûŪûöĀöā!{ä¨ũäS{ė¸=qžüÜ~ņĨũĘÚS§ĖąuÆ/EP†2” E(B ‡<øsßŖuß#0x`Ā@2 æčSžVđƒˆ€SJaēŒB “KabFļŸaz ķáķ†@4Ā_‘b” ‹éęȧttĨ,­ÃėߞĄŦTĐ@ũ• Ĩ(Î.Ĩ É꾒§CÉ YQFĀ<đ!ƒyûÕ %Ą Ņuš!””’P„‚PPōBō‚¯øBFÅ1$ņjDđÄé3š+C‘>ƚ%EĮȃ/)¨2ꉎ„a&ŪČ$&”…’cŒ-ˈŊZFGŠaÆwKŌ$qIËJ€ę¯íÄ ū Ī˙žŋžģžû3<{=<û{xæjxæJøõ´=ŊĨ×ZŖũ­5fãöLęPƒ9¤ ŗ0‹ÜUš~4 ߄-–ū|fŸm@+uÍÃ<´a"0ëˇIŠC栖bĖ(u?úČÚā]õą;lVJĄP_­Ķēr”Όģ9¤q‹'JĨÔBĨž i9 š‚"‹OÎkŠ-E4Ō¸›šŌ2õmĀrîâģņ n:˜(ĄUĻBĢž ūėšģîļZĨĻĘLčÔĪÂ˙wW)40)tĘ\˜Ģ÷+Đ €ŠŧT_Í?ˆGŲĒ{wG÷€N‘)tč éĐšųjÆëƒ^âüÃ0ā~_jÄ 4h‹L˛í|ķų…ŧ‰ī —ûsI…ŽEÚ@ ͐ŗmũĘĸvØøúų;ŌĖŖ8R™ģ^jû"Õ ŠˆŦRC…6>|+ˇ‘āWÂAЌõp ĀĄ)öO'Ę-‘Šaų_ëÕ[Âųē>šwˇĢ(e5Hļ5ĩˆ‹:ä§Īļ‘āÂyĮz8ã–Ģ$ƒŒC“yåķkû†U…樏‡GæĢ.Í%˜ôž ú)xŋØâīļr|ĒOkļ5ģ }MßëR9Ŧ?ÅHŌąĀ5`_€Q üŽ.i ŽPÄ7Æ|HrĢÉel$ЛĢÁĖ\$XņT)3Tš%?•ŌqoŲŋË*V’%ŽJ‹‰úų(Kųq OôĢķíÂq/‡në”B’¸ãÎ]gŠ_‘`ĪGR|ķ%mÍ´bZĶÕ2ĢûßæíwĖ›@{ļ?XĮ&Ú^~Čņ ÆMTPI˛F™$Š->‚ŠŌŽ|§˜#0ŽM&ķØ‘Ãd'Q(Ĩ$‘4éÁÅ Ļ0Sēāį§‚cÛs„:yę†ŧUōę]ĮMj 0WæÁûGÉąŲĻ—ī†o l` k°‚°„;4ĻÅ íXÍŊ {ĮvGž‡ņŗéî!@,æČĐ'Ûí$‘iKčüqOĻīaHOvā†}8Xgúޞ DBës9´§“åĘÂ6û)ØËjŦ2ãí­"4~, ‡*Ú§3›0CŽlĻσZĖ(cՂUåĩ@ \ԌöŒõivƒŨ™!Č)̓Ēr+…§LVķ€ƒė7ŽĘ1ōĨ Ö˙ŲÚjžŽōŖüIENDŽB`‚libspng-0.7.4/tests/images/f99n0g04.png000066400000000000000000000006521442617636000174710ustar00rootroot00000000000000‰PNG  IHDR “áČ)qIDATxœuQąNAŨåŽdj´8ģģR4TVЉĩ‰‰? Æ0Š_@ü=ũĄ°3 ą2FŽÄ‚]ėŦÜŨ ւŦŗ`0AģĖííĖŧyoÆCsæOŧWĻČöŸgˇ {'`›îŒ]|7fĄDr2pąG ,—\`ĘKaB&ûæ@ËyÆ0” É{—s˜4Úi¯Ÿv[Éq@÷ã˜2ˆ(+ÄĶM­2˙ ^ĖX@KËaŽ@F`ˆFęÁ÷”96ˆ**YųÕĢ6—ŌZ)yˇYÛĀŲŖĩhÚV+%y33åOŋ°đÖÃ|@€&2ĐYq~({ÚH`ęGÚ:ßų j­Ö”!įáėᕐaLŒAvdŪeĪ+'ÜĄixĖlûŦ‘rāĘyÚē¨ü%_*Ģ´Ōvō€|[ČúI ´Š[7ĶND)SŽ÷ ?%ģ]M.ëõë¤VųoQ㎐B ÄdšžĢž’‘’/?ÛßÍÔoŗ[„įt¤IENDŽB`‚libspng-0.7.4/tests/images/g03n0g16.png000066400000000000000000000005311442617636000174520ustar00rootroot00000000000000‰PNG  IHDR ųkgAMAˆ¸ŊÅI°IDATxœĩÕM Â0āWŦZEP(Å]ÁS4—đ´^"ˇ\"‚Ĩâ_mu!A3Ctčë7Ĩ´Ņũ­ĸHīĨÔûĸĐû¸iôÁ|kҰ€,ŗP1 ŖQ `8  ž‚ÛMôû‚$ ôz× NAˇë)đ]á˙WÔõqŦ÷më) ¯ōåâĐ* N  Å×Ģ_Ā×‚Ų X.ÕTõyeēc§yžZŠBJu2y…õúŊ“Ō"8ŸM˜N7ĩ€E@WPĪ Mw;Š N'“`2Ԩō}&8MUĨúįe¯Ļ€ĪÅeéĀÛm `ą<uŒ†+DŨˇIENDŽB`‚libspng-0.7.4/tests/images/g03n2c08.png000066400000000000000000000005621442617636000174550ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMAˆ¸ŊÅI°)IDATxœå–MŠÂ@FĢ1ūDw.ŧÄxë!ú.2‚dFgFãˆÉ$äø˛V•JĨ_ŋ.íb+gđÆģâúGq98`>/Ž/ ß€‹€ŅˆV‚aôû"€ ū ŋ×ĒAˇ+Ô„ĄP : Î Ũǁ P Z-@ĩTƒfSŧŸAˆ2ø…~4¸@]5ÉāęĒȀú_>đŒÁtęĖlšĖŽ1333$ƒRĀdâVĢ8$ĢûlB'4I˛^ĮųĸųĘø7ƒņØm6ŠMV78āąYŗáĐmˇtCģ|—wc˜Ųn'[Î|•ĸ(ûŠĪ%ˇ ƒjûöПžAĨ ƒĪēd@ˇåē ŽZžY#[ßjIENDŽB`‚libspng-0.7.4/tests/images/g03n3p04.png000066400000000000000000000003261442617636000174650ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMAˆ¸ŊÅI°PLTE˙˙˙˙ČČ­­ŨŨ˙˙˙˙˙˙Ũ˙˙­˙1ē ˆcIDATxœc(% 0A `@`ú¨‚™@W,@G.@ĐpČ”Š`č06`FRŅĐÁaĖl€b\¤Ĩ%¤ÁU@ØĀ lĖ@-H*ŌŌāî@ SRĶpŊæĩĢIENDŽB`‚libspng-0.7.4/tests/images/g04n0g16.png000066400000000000000000000005531442617636000174570ustar00rootroot00000000000000‰PNG  IHDR ųkgAMA¯Č7Šé"IDATxœ­”9Â0E'",$š4)RŌpN€Oíp š ea Ed…™1YƅĨųRžŸŋm hžĩÆķn‡į°Žq°ŨÂāĸ0@¨qø~ã`ĩō4X. (`ąp4xŊp0Ÿ{D‘ŖĖfžĶŠŖí@0W€ØÁdâi Äœ žĪ?ŒĮxnGú/Üī‚P  (€.Ņ ‡Ėāņ°l6‡ƒI•2Ī+3°ÖëãŅėJimvŅ ģÂéô=}?íĖ€ļÜwĻįsŸwį[ ĒĘH’Ë…n1(KtÄ1@’d™RZ+Õ×Č ŠÂfpŊšYkĄƒ<ˇ_á×bˇFŖa3Č2OƒũŪ @ >)4‰(Ĩc€IENDŽB`‚libspng-0.7.4/tests/images/g04n2c08.png000066400000000000000000000005711442617636000174560ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA¯Č7Šé0IDATxœÕ–=nÂ@Fŋ•ĖŸŒD-āņĄ}„€‚‚†‚‚H(R"~’Ȱƒ_œOq“)ŦõxwŪŧÆ!SuÁ—4TįŸĒĶŅ'æķęü3ėp0R%FĀ``\ƒ~߸ql\ƒ^Ώ6€ >`ˇk\āΠĶ1d@WÔn›w˙ߏ­– ƒėGƒ/Čģ1Ā5Ā+:@ž l›üÅ`6 ’‹rD’”^_]ƒ(ĘĶiX.ŗâ"¯ž–d@3ģVĢė1yoüd@€o3˜LÂzũĐdR ‘Á÷fĨņ8l6?VÔFŖpaHÚnŗŧnre¨nīu€ŨŽ|4-<‹Aou€ßŧ6 ƒ—Ļd@ËMœ‘MW†Hƒĸ×IENDŽB`‚libspng-0.7.4/tests/images/g04n3p04.png000066400000000000000000000003331442617636000174640ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMA¯Č7ŠéPLTE˙˙˙Ô˙ÔÔ˙˙ēē˙˙˙˙˙˙›˙››"č,‚hIDATxœŊÎÁ €0DŅ!°šÛ¤[°€\æžĶļ` ļ`ˇN„õĒā?eq¨]Q- q@īąŠSM‡ÅĨŠš"ß Ģą–DĢ‰ŦCôܛ“6IJ Ґ ˆÂ}ū‡—â‹ĩ~ u§•IENDŽB`‚libspng-0.7.4/tests/images/g05n0g16.png000066400000000000000000000005231442617636000174550ustar00rootroot00000000000000‰PNG  IHDR ųkgAMAÖØÔOX2 IDATxœ­ÕŊ ƒ0đŅ~ HĮÎíŌÅ7iôÅŲ>AA ĸļUė As§ GšA/˙ųå@ ÆQ#Ė9€9ŋßÍšü~ͅËEXŪ@öŊšp:Ų°˜’„)Āqė)8™ÜW€Sp80Ÿ/Ā)Øī™nā#ėvžWĀ˙ܧ Š<8`˜ü2ĩ­Cđ~Û8€\x8ah`˛,Īu­”ūŧA×­ nˇšV @_‰`+ (ŽW]X¸ËË&ęÚ*ĀË&Ž5”š-Čé~> ‘ĻΧRË#Đģ–åtŸ›pDP×ÛGXDđzņˆ ĒЂd ?Áí_„fAŗāIENDŽB`‚libspng-0.7.4/tests/images/g05n2c08.png000066400000000000000000000005361442617636000174600ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMAÖØÔOX2IDATxœå–9Â0EĮRØBDAGC‰h¨r8hnB¸ ĸ@ė‹BÁ"Bō$pĮTÎøĮ/Īąĸ¸ÜĒËĖdŽē?ŽnGg Õũ9ä ¸čvi%(Ž.ītD€j$"€ Đn‹Õ ŽE€jĐj‰Õ@¨ÍĻ ƒä đ2@5¨×EÛĸ`Wč“Áō öĐf@Õ Vƒ *2 @°St€ü‡Ašēé´¸ÆÄĖ˞įå/Ŗ‘ûčØäšôkđ‹Ál–‡Žā”yĐĄ(ŋäBĮßāK@šČ`įčõœ™õûnąČ\Ū"2Øz–ˡģŗŌā^d°ņxŦ!E"€ Vf@ËĄžE7j9M™œ,WIENDŽB`‚libspng-0.7.4/tests/images/g05n3p04.png000066400000000000000000000003161442617636000174660ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMAÖØÔOX2PLTE˙˙ĖĖ˙Ė˙ŽŽ˙˙‹‹˙˙˙˙˙˙‹˙sâ$’[IDATxœcč‚P pA `@`ú¨P‚™@W,@Gi@` pČ”Š00vq`AVÁ`ėÂâ€C”—”ÃU Š``gjARQ^w˛™*Ôx;Jí UIENDŽB`‚libspng-0.7.4/tests/images/g07n0g16.png000066400000000000000000000005011442617636000174530ustar00rootroot00000000000000‰PNG  IHDR ųkgAMAp‰;XVøIDATxœÍ•ģ „0 † Bá%h™€¨X€IČ\™‰1-ˆ§„¸åDl ĨšŋILņųÃŲ9Pâ8j-„Z×ĩZ{ÛĻ>Č2x n@iúĀÆ$–qliE–:€Ö - t­AŦĢā LZߡ4ĐŒ öŨĐ˙ÎĶdi€Ä`YŪ8ÄĀ€ Ü…}SUŒ1ĀųÕ@ÍŖAYŪÛhgpöhšĸ÷kČ æų€īœŸ‚üā\"°;ĄČæ9@ž3Æ9€BČu xøC‘]Ûö<åj“'™Á8>ŋÂ]Č †Á @ úŪŌ ëė >ÅĄÔĐ,s,IENDŽB`‚libspng-0.7.4/tests/images/g07n2c08.png000066400000000000000000000005241442617636000174570ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMAp‰;XV IDATxœÕ–A ƒ0E'"‰ĸ¸õš€+/āIęąr&¯!Ũ–m)ØE+U:34`;Ė"Œ!ĪįWPMė"pÅ)~~āĮņ ʒŸÁ~\( !C@žŖ“@I ˛LˆRƒ4¤b2¸†ėf€I" ôˆÄ€Ÿ30FžE˙ow0—R2Á~hpķŨ2@Z Ā ‰Ø6fÕMŖž‹Wĩœ×^u­ļPƒ ëĻĒR”|†ĖÄŪšyũ?i× ` DlkũnkYĢ´žĪuķšKŽcôŨ,īˇīūŽø5ĘĀāU(ƒs(28… ô3Ęā€Z?ᥠIENDŽB`‚libspng-0.7.4/tests/images/g07n3p04.png000066400000000000000000000003171442617636000174710ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMAp‰;XVPLTE˙˙œœŋŋ˙v˙vv˙˙˙˙˙˙˙˙ŋ˙N—Tl\IDATxœcč‚4 PA `@`ú¨0 €Ģ@ ŖŠP ˜ pČ”Š˜0S‰ EXģ (//(Wbb‚¨€ 00 Ģ``gRBQQ^w˛™*# kŊér=ĪIENDŽB`‚libspng-0.7.4/tests/images/g10n0g16.png000066400000000000000000000004061442617636000174510ustar00rootroot00000000000000‰PNG  IHDR ųkgAMA† 1č–_ŊIDATxœí•1„ E? ,=†\ÃŌŌ!ō^ÃŌKXHbÁ„Å™DŲdc˛ūfϘyü΁HškMķq¤ye--H‰¤ø€âœ¸ÚwZ¨ë_<ÜÁƒ¯ JMüGŽŖ iŌ Š=ØļB‚;Ũƒa˜Ļž×lú>ÆÆhÖl‚yîēŗÆøŧ,“üđkdĮ”41Æņ{áĸmi!§,‹RūüĄIöĪ´ŽįŲ×ĪĢ ŧę˜ŋåü–ˆIENDŽB`‚libspng-0.7.4/tests/images/g10n2c08.png000066400000000000000000000004351442617636000174520ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA† 1č–_ÔIDATxœí–1 „0D˙ íö–ÁÖŌûüÍ}ŧ‚Į°õ ģÅBļp 3Høœ*M&ÃS‘-…ÅŽ‡ũgØÎŪ ĪÃūÜO 7C ĸ@áč¨T  PÄ ("kp3pēėę°É _‡Áøą (āüd PĀu )´-wŨzCDD2O5 š†ũH3o횯ißÛēævŗëøÎtv—ĄŠ|GÖSMƒ˛d"Ē*û?¸l kŒãbĩl“Îūėët RũUüÔ­MTŪߏžIENDŽB`‚libspng-0.7.4/tests/images/g10n3p04.png000066400000000000000000000003261442617636000174630ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMA† 1č–_PLTEŠŠ˙˙˙Ē˙TT˙˙˙˙˙˙˙˙U˙îÛZcIDATxœcč‚P H% `@`ú¨‚™@W,@G.@` pČ”Š00NK`CVÁ`œÆ–€Ļ‚ĸĘË ĘĶ *ā@-H*ØŲ€†"Š(/‡ģY€LFâ…É™:IENDŽB`‚libspng-0.7.4/tests/images/g25n0g16.png000066400000000000000000000005771442617636000174700ustar00rootroot00000000000000‰PNG  IHDR ųkgAMAАøTˆO6IDATxœĨ”Ë Â0 @Ũ@|"Ägnā:]î狐5 ņ‡8DQą-YøP×VōōęVŪo@E¸N\o6¸VEĸ ¨íV ÆjˇķŸ4Øīef 0ƒÃáO ˆcĄÁņˆ†Đ@ `§ĶŸį3i(ĄÁå‚ÍĻĐāz•˜´ZBƒÛ 7ÚmĄÁũî”eĀ€\Ķ×Ė Üčtüfđ|ú4˜tģ~3 Cęõl^­f3{oLũkeU…ũ>Āb‘Ļî0 ­]βēǃÄ ­ĩÖ Ëæķo€1·zŊ0Ņ}ËåweŒ››}&ģ%ΧS€<ˇÛ’¤$›m ‡6¯×6ģųģ4p€_4üf@ŒĮBē`2‘|ģ?š 8¤IENDŽB`‚libspng-0.7.4/tests/images/g25n2c08.png000066400000000000000000000006251442617636000174610ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMAАøTˆOLIDATxœ­•Ëm1@Į‹ á#ĘØŽ8Pu -` ā@ TĘ@|„ˆH‚sĀZŧÄOĘHų0Œgįéi,aœÄÃÜä&^‹—íęŽŨĨ€°Ũ§ÁúáÚSȀY¯ß@'%ƒ ’Čā ũ†@°V ƒ÷T2H ƒčoĩ”2 @ŗŠÁ'ôˇÛJ\´€¨“^_ā‚^|A§ŖÁU   ƒočīv•2ø`š4"2›36"yũ­A¯į“˛4ķš “xŊē pĪ‹Â<Ÿ ˛›HôôûūˆHQ˜Å‰øŸØÔ bĮŌ_`8Ģ,ŨßĸŸ^-ƒv@€ÁĀ'Ģ•›NÍ=yLĖëĶ…w@[Ģ"˛^]áã s2 Āpd@ũŖ‘@Ô?+ZƒÉD ƒ_ˆ-a2šwIENDŽB`‚libspng-0.7.4/tests/images/g25n3p04.png000066400000000000000000000003271442617636000174720ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMAАøTˆOPLTE--˙˙˙\˙˙˙˙˙\\˙˙˙˙˙RQ×ådIDATxœŊÎą €0 DQ[J‘Ö+° ¸Č ŦĀ ´”^msQ$s5Hųåų–ČІ„­:P Š5”‚‡Bš) oe ¯p/)Fq×jĒSĖ!*Î$BBĘ?xø(:5tfĮ;´PØIENDŽB`‚libspng-0.7.4/tests/images/meson.build000066400000000000000000000237431442617636000177530ustar00rootroot00000000000000test('example_notext', example_exe, args : files('basi0g08.png')) test('example_text', example_exe, args : files('ct1n0g04.png')) test('basi0g01', test_exe, args: files('basi0g01.png')) test('basi0g02', test_exe, args: files('basi0g02.png')) test('basi0g04', test_exe, args: files('basi0g04.png')) test('basi0g08', test_exe, args: files('basi0g08.png')) test('basi0g16', test_exe, args: files('basi0g16.png')) test('basi2c08', test_exe, args: files('basi2c08.png')) test('basi2c16', test_exe, args: files('basi2c16.png')) test('basi3p01', test_exe, args: files('basi3p01.png')) test('basi3p02', test_exe, args: files('basi3p02.png')) test('basi3p04', test_exe, args: files('basi3p04.png')) test('basi3p08', test_exe, args: files('basi3p08.png')) test('basi4a08', test_exe, args: files('basi4a08.png')) test('basi4a16', test_exe, args: files('basi4a16.png')) test('basi6a08', test_exe, args: files('basi6a08.png')) test('basi6a16', test_exe, args: files('basi6a16.png')) test('basn0g01', test_exe, args: files('basn0g01.png')) test('basn0g02', test_exe, args: files('basn0g02.png')) test('basn0g04', test_exe, args: files('basn0g04.png')) test('basn0g08', test_exe, args: files('basn0g08.png')) test('basn0g16', test_exe, args: files('basn0g16.png')) test('basn2c08', test_exe, args: files('basn2c08.png')) test('basn2c16', test_exe, args: files('basn2c16.png')) test('basn3p01', test_exe, args: files('basn3p01.png')) test('basn3p02', test_exe, args: files('basn3p02.png')) test('basn3p04', test_exe, args: files('basn3p04.png')) test('basn3p08', test_exe, args: files('basn3p08.png')) test('basn4a08', test_exe, args: files('basn4a08.png')) test('basn4a16', test_exe, args: files('basn4a16.png')) test('basn6a08', test_exe, args: files('basn6a08.png')) test('basn6a16', test_exe, args: files('basn6a16.png')) test('bgai4a08', test_exe, args: files('bgai4a08.png')) test('bgai4a16', test_exe, args: files('bgai4a16.png')) test('bgan6a08', test_exe, args: files('bgan6a08.png')) test('bgan6a16', test_exe, args: files('bgan6a16.png')) test('bgbn4a08', test_exe, args: files('bgbn4a08.png')) test('bggn4a16', test_exe, args: files('bggn4a16.png')) test('bgwn6a08', test_exe, args: files('bgwn6a08.png')) test('bgyn6a16', test_exe, args: files('bgyn6a16.png')) test('ccwn2c08', test_exe, args: files('ccwn2c08.png')) test('ccwn3p08', test_exe, args: files('ccwn3p08.png')) test('cdfn2c08', test_exe, args: files('cdfn2c08.png')) test('cdhn2c08', test_exe, args: files('cdhn2c08.png')) test('cdsn2c08', test_exe, args: files('cdsn2c08.png')) test('cdun2c08', test_exe, args: files('cdun2c08.png')) test('ch1n3p04', test_exe, args: files('ch1n3p04.png')) test('ch2n3p08', test_exe, args: files('ch2n3p08.png')) test('cm0n0g04', test_exe, args: files('cm0n0g04.png')) test('cm7n0g04', test_exe, args: files('cm7n0g04.png')) test('cm9n0g04', test_exe, args: files('cm9n0g04.png')) test('cs3n2c16', test_exe, args: files('cs3n2c16.png')) test('cs3n3p08', test_exe, args: files('cs3n3p08.png')) test('cs5n2c08', test_exe, args: files('cs5n2c08.png')) test('cs5n3p08', test_exe, args: files('cs5n3p08.png')) test('cs8n2c08', test_exe, args: files('cs8n2c08.png')) test('cs8n3p08', test_exe, args: files('cs8n3p08.png')) test('ct0n0g04', test_exe, args: files('ct0n0g04.png')) test('ct1n0g04', test_exe, args: files('ct1n0g04.png')) test('cten0g04', test_exe, args: files('cten0g04.png')) test('ctfn0g04', test_exe, args: files('ctfn0g04.png')) test('ctgn0g04', test_exe, args: files('ctgn0g04.png')) test('cthn0g04', test_exe, args: files('cthn0g04.png')) test('ctjn0g04', test_exe, args: files('ctjn0g04.png')) test('ctzn0g04', test_exe, args: files('ctzn0g04.png')) test('exif2c08', test_exe, args: files('exif2c08.png')) test('f00n0g08', test_exe, args: files('f00n0g08.png')) test('f00n2c08', test_exe, args: files('f00n2c08.png')) test('f01n0g08', test_exe, args: files('f01n0g08.png')) test('f01n2c08', test_exe, args: files('f01n2c08.png')) test('f02n0g08', test_exe, args: files('f02n0g08.png')) test('f02n2c08', test_exe, args: files('f02n2c08.png')) test('f03n0g08', test_exe, args: files('f03n0g08.png')) test('f03n2c08', test_exe, args: files('f03n2c08.png')) test('f04n0g08', test_exe, args: files('f04n0g08.png')) test('f04n2c08', test_exe, args: files('f04n2c08.png')) test('f99n0g04', test_exe, args: files('f99n0g04.png')) test('g03n0g16', test_exe, args: files('g03n0g16.png')) test('g03n2c08', test_exe, args: files('g03n2c08.png')) test('g03n3p04', test_exe, args: files('g03n3p04.png')) test('g04n0g16', test_exe, args: files('g04n0g16.png')) test('g04n2c08', test_exe, args: files('g04n2c08.png')) test('g04n3p04', test_exe, args: files('g04n3p04.png')) test('g05n0g16', test_exe, args: files('g05n0g16.png')) test('g05n2c08', test_exe, args: files('g05n2c08.png')) test('g05n3p04', test_exe, args: files('g05n3p04.png')) test('g07n0g16', test_exe, args: files('g07n0g16.png')) test('g07n2c08', test_exe, args: files('g07n2c08.png')) test('g07n3p04', test_exe, args: files('g07n3p04.png')) test('g10n0g16', test_exe, args: files('g10n0g16.png')) test('g10n2c08', test_exe, args: files('g10n2c08.png')) test('g10n3p04', test_exe, args: files('g10n3p04.png')) test('g25n0g16', test_exe, args: files('g25n0g16.png')) test('g25n2c08', test_exe, args: files('g25n2c08.png')) test('g25n3p04', test_exe, args: files('g25n3p04.png')) test('oi1n0g16', test_exe, args: files('oi1n0g16.png')) test('oi1n2c16', test_exe, args: files('oi1n2c16.png')) test('oi2n0g16', test_exe, args: files('oi2n0g16.png')) test('oi2n2c16', test_exe, args: files('oi2n2c16.png')) test('oi4n0g16', test_exe, args: files('oi4n0g16.png')) test('oi4n2c16', test_exe, args: files('oi4n2c16.png')) test('oi9n0g16', test_exe, args: files('oi9n0g16.png')) test('oi9n2c16', test_exe, args: files('oi9n2c16.png')) test('pp0n2c16', test_exe, args: files('pp0n2c16.png')) test('pp0n6a08', test_exe, args: files('pp0n6a08.png')) test('ps1n0g08', test_exe, args: files('ps1n0g08.png')) test('ps1n2c16', test_exe, args: files('ps1n2c16.png')) test('ps2n0g08', test_exe, args: files('ps2n0g08.png')) test('ps2n2c16', test_exe, args: files('ps2n2c16.png')) test('s01i3p01', test_exe, args: files('s01i3p01.png')) test('s01n3p01', test_exe, args: files('s01n3p01.png')) test('s02i3p01', test_exe, args: files('s02i3p01.png')) test('s02n3p01', test_exe, args: files('s02n3p01.png')) test('s03i3p01', test_exe, args: files('s03i3p01.png')) test('s03n3p01', test_exe, args: files('s03n3p01.png')) test('s04i3p01', test_exe, args: files('s04i3p01.png')) test('s04n3p01', test_exe, args: files('s04n3p01.png')) test('s05i3p02', test_exe, args: files('s05i3p02.png')) test('s05n3p02', test_exe, args: files('s05n3p02.png')) test('s06i3p02', test_exe, args: files('s06i3p02.png')) test('s06n3p02', test_exe, args: files('s06n3p02.png')) test('s07i3p02', test_exe, args: files('s07i3p02.png')) test('s07n3p02', test_exe, args: files('s07n3p02.png')) test('s08i3p02', test_exe, args: files('s08i3p02.png')) test('s08n3p02', test_exe, args: files('s08n3p02.png')) test('s09i3p02', test_exe, args: files('s09i3p02.png')) test('s09n3p02', test_exe, args: files('s09n3p02.png')) test('s32i3p04', test_exe, args: files('s32i3p04.png')) test('s32n3p04', test_exe, args: files('s32n3p04.png')) test('s33i3p04', test_exe, args: files('s33i3p04.png')) test('s33n3p04', test_exe, args: files('s33n3p04.png')) test('s34i3p04', test_exe, args: files('s34i3p04.png')) test('s34n3p04', test_exe, args: files('s34n3p04.png')) test('s35i3p04', test_exe, args: files('s35i3p04.png')) test('s35n3p04', test_exe, args: files('s35n3p04.png')) test('s36i3p04', test_exe, args: files('s36i3p04.png')) test('s36n3p04', test_exe, args: files('s36n3p04.png')) test('s37i3p04', test_exe, args: files('s37i3p04.png')) test('s37n3p04', test_exe, args: files('s37n3p04.png')) test('s38i3p04', test_exe, args: files('s38i3p04.png')) test('s38n3p04', test_exe, args: files('s38n3p04.png')) test('s39i3p04', test_exe, args: files('s39i3p04.png')) test('s39n3p04', test_exe, args: files('s39n3p04.png')) test('s40i3p04', test_exe, args: files('s40i3p04.png')) test('s40n3p04', test_exe, args: files('s40n3p04.png')) test('tbbn0g04', test_exe, args: files('tbbn0g04.png')) test('tbbn2c16', test_exe, args: files('tbbn2c16.png')) test('tbbn3p08', test_exe, args: files('tbbn3p08.png')) test('tbgn2c16', test_exe, args: files('tbgn2c16.png')) test('tbgn3p08', test_exe, args: files('tbgn3p08.png')) test('tbrn2c08', test_exe, args: files('tbrn2c08.png')) test('tbwn0g16', test_exe, args: files('tbwn0g16.png')) test('tbwn3p08', test_exe, args: files('tbwn3p08.png')) test('tbyn3p08', test_exe, args: files('tbyn3p08.png')) test('tm3n3p02', test_exe, args: files('tm3n3p02.png')) test('tp0n0g08', test_exe, args: files('tp0n0g08.png')) test('tp0n2c08', test_exe, args: files('tp0n2c08.png')) test('tp0n3p08', test_exe, args: files('tp0n3p08.png')) test('tp1n3p08', test_exe, args: files('tp1n3p08.png')) test('xc1n0g08', test_exe, args: files('xc1n0g08.png'), should_fail : true) test('xc9n2c08', test_exe, args: files('xc9n2c08.png'), should_fail : true) test('xcrn0g04', test_exe, args: files('xcrn0g04.png'), should_fail : true) test('xcsn0g01', test_exe, args: files('xcsn0g01.png'), should_fail : true) test('xd0n2c08', test_exe, args: files('xd0n2c08.png'), should_fail : true) test('xd3n2c08', test_exe, args: files('xd3n2c08.png'), should_fail : true) test('xd9n2c08', test_exe, args: files('xd9n2c08.png'), should_fail : true) test('xdtn0g01', test_exe, args: files('xdtn0g01.png'), should_fail : true) test('xhdn0g08', test_exe, args: files('xhdn0g08.png'), should_fail : true) test('xlfn0g04', test_exe, args: files('xlfn0g04.png'), should_fail : true) test('xs1n0g01', test_exe, args: files('xs1n0g01.png'), should_fail : true) test('xs2n0g01', test_exe, args: files('xs2n0g01.png'), should_fail : true) test('xs4n0g01', test_exe, args: files('xs4n0g01.png'), should_fail : true) test('xs7n0g01', test_exe, args: files('xs7n0g01.png'), should_fail : true) test('z00n2c08', test_exe, args: files('z00n2c08.png')) test('z03n2c08', test_exe, args: files('z03n2c08.png')) test('z06n2c08', test_exe, args: files('z06n2c08.png')) test('z09n2c08', test_exe, args: files('z09n2c08.png')) libspng-0.7.4/tests/images/oi1n0g16.png000066400000000000000000000002471442617636000175550ustar00rootroot00000000000000‰PNG  IHDR ųkgAMA† 1č–_^IDATxœÕŌ1 Ā0 CQ9[īÆÜ “Ā({2Õ*ŖŲ?8€§¸Wc :‚`™Ũ‚ö@ĘčÜB&@ˇ =2玁 Đ-h¤L`?oėOô8ĪØKØ_Ž+Ÿ×IENDŽB`‚libspng-0.7.4/tests/images/oi1n2c16.png000066400000000000000000000004561442617636000175550ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_åIDATxœÕ–Á ƒ0D§āAË~ˇũ­ö–’†&f“Ėz†Eä=Ö B>/āšīdrs~='3_ÎâķZwˇëÎt(pĸ3—ķíōp]Ņ`힁_čėōytϰĀ?tĻä(Cį\l5čė2õčLģčĖŠg  ķI°@g.`‡Î(`øĮîļgį ôDgī&ĐŊ›Ā(ô(`÷‹Îû60ŊY`zŗĀlô(P4Đ9¯Ü€:{ą€zą€*zȟmtÎ3đ€ÎžøAOŧĄ3ŋæBū^ģÄėIENDŽB`‚libspng-0.7.4/tests/images/oi2n0g16.png000066400000000000000000000002631442617636000175540ustar00rootroot00000000000000‰PNG  IHDR ųkgAMA† 1č–_@IDATxœÕŌ1 Ā0 CQ9[īÆÜ “Ā({2Õ*ŖŲ?8€§¸Wc :‚`™Ũ‚ö@ĘčÜB&@ˇ =2:°Ā3IDAT玁 Đ-h¤L`?oėOô8ĪØKØ_˛z_}IENDŽB`‚libspng-0.7.4/tests/images/oi2n2c16.png000066400000000000000000000004721442617636000175540ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_€IDATxœÕ–Á ƒ0D§āAË~ˇũ­ö–’†&f“Ėz†Eä=Ö B>/āšīdrs~='3_ÎâķZwˇëÎt(pĸ3—ķíōp]Ņ`힁_čėōytϰĀ?tĻä(Cį\l5čė2õčLģčĖŠg  ķI°@g™ˆ seIDAT.`‡Î(`øĮîļgį ôDgī&ĐŊ›Ā(ô(`÷‹Îû60ŊY`zŗĀlô(P4Đ9¯Ü€:{ą€zą€*zȟmtÎ3đ€ÎžøAOŧĄ3ŋæBū^MwžbIENDŽB`‚libspng-0.7.4/tests/images/oi4n0g16.png000066400000000000000000000003131442617636000175520ustar00rootroot00000000000000‰PNG  IHDR ųkgAMA† 1č–_IDATxœÕŌ1 Ā0 CQ9[īÆÜ “Ā({2Õ*ŖŲ?8‡m“ßIDAT€§¸Wc :‚`™Ũ‚ö@ĘčÜB&@ˇ =ÎŖËėIDAT2Åmf IDAT玁 Đ-h¤L`?oėOô8ĪØKØ_˛z_}IENDŽB`‚libspng-0.7.4/tests/images/oi4n2c16.png000066400000000000000000000005221442617636000175520ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_cIDATxœÕ–Á ƒ0D§āAË~ˇũ­ö–’†&f“Ėz†Eä=Ö B>/āšīdrs~='3_ÎâķZwˇëÎt(pĸ3—ķíōp]Ņ`힁_čėōytϰĀ?tĻä(CnTZXIDATį\l5čė2õčLģčĖŠg  ķI°@gečÅæcIDAT.`‡Î(`øĮîļgį ôDgī&ĐŊ›Ā(ô(`÷‹Îû60ŊY`zŗĀlô(P4Đ9¯Ü€:{ą€zą€*zȟmtÎ3đ€ÎžøAOŧĄ3ŋæB ââ)IDATū^z zIENDŽB`‚libspng-0.7.4/tests/images/oi9n0g16.png000066400000000000000000000024031442617636000175610ustar00rootroot00000000000000‰PNG  IHDR ųkgAMA† 1č–_IDATxvæ„æIDATœŅĸ‡IDATÕہ[ŗIDATŌ@åÎIDAT1yæ}ŌIDAT Čí”öIDATĀŗ\ŋXIDAT0áMDIDAT !Ž1ÃIDATCĮímÂIDATQ4TŠIDAT9w=õāIDAT[ԁõ”IDATī‚IDATč‚EIDATÆZ?mIDATܧ]ãIDAT ūîŪIDAT“A>ŋIDATĀŗ\ŋXIDAT(ÕIDAT{īīÕ\IDAT2āī,hIDATÕہ[ŗIDAT*탴>IDAT/UšņIDATŖgįēIDATŲ×7˜IDATU?ãYIDAT?ž^PÕIDAT8:ÅvIDAT€Å€ūČIDAT§`ŠKŖIDAT¸í‚FVIDATWŨ7šŋIDATŦ†<6IDATŦ†<6IDATcüƒM IDAT ūîŪIDAT:î4¤ZIDAT‚+ŽŸäIDAT`eаIDATK>1IDAT&ãõÚIDAT™ĄëVIDAT(8}čIDATŨĐŹIDAT‚+ŽŸäIDATö|æ*ÁIDAT@^äŋIDATĖēęķsIDATz˜čåĘIDAT;TåžIDAT†,ã[ũIDATE.ŽČ÷IDATä_[‰IDAT=pP1ųIDATÖGˆ IDAT ūîŪIDATU?ãYIDAT5mŒIDATB°ę]TIDAT(8}čIDAT>éY`CIDAT/ƒé@ąIDATāˆ2ŸIDATš8â˛IDATī‚IDATdbįØŠIDATr–3møIDATsá4]nIDAT~Ÿ…!ĶIDAT;TåžIDAT=pP1ųIDAT'2ȃIDAT3—čūIDAT_Ķė1IDATÎTä’_IDATâf<ūŧIDATķ ŒŪNIDATZŖ†ÅIDATwæY™wIDATˇ}=[ĮIDATÆ6ÄIDATëāFIDATÎTä’_IDATtPČÍIDAT(ÕIDATpx= ÔIDATĸāŋ,IDAT3—čūIDAT—FS{IDATķ ŒŪNIDATíöƒã-IDATō{‹îØIDATpx= ÔIDAT]=âPĄIDATŅŲėŸĒIDAT_?M~IDAT`eаIDATķ ŒŪNIDAT˛ W¯HIDAT˛‡Î^IDAT_Ķė1IDATč†éĸIDAT끄ĶģIDATō{‹îØIDATÆ6ÄIDATyá´pIDATtPČÍIDATĻ{5IDAT°ãYÎdIDATĀŗ\ŋXIDAT?ž^PÕIDATtPČÍIDATĻ{5IDATä_[‰IDATLSÕ(IDAT(ÕIDATCĮímÂIDATįV 3IDAT\Jå`7IDATll1IDAT(8}čIDATļ :kQIDATgûî‰IDATįV 3IDAT V‰UIDATô’čKíIDATDY‰øaIDATgûî‰IDATī‚IDAT&ú5øIDATĐŽë¯<IDATĨ0pIDATŊč˛ŲIDAT›Oå7$IDATĀŗ\ŋXIDAT(ÕIDATô’čKíIDAT(ÕIDAT`eаIDAT÷ áWIDATK>1IDAT;TåžIDAT‹RR'@IDATÎTä’_IDATûWV|IDATģ1‚uIDAT6į‚čqIDAT0áMDIDATą1,RIDATĻ†’IDATŊč˛ŲIDATY:”¸IDAT`eаIDATŌ7@‹IDATz˜čåĘIDATŗzPŸŪIDATĀŗ\ŋXIDATllIDATz˜čåĘIDAT2⩕IDATČŊ‡7jIDATŸHˆķ=IDAT_?M~IDATm;` IDATtPČÍIDATÎTä’_IDAT3—čūIDATĸ]´IDAT𕅏ôIDAT€Å€ūČIDATÎTä’_IDATž?ÃĢIDAT&ãõÚIDATø›^ÆIDATA)ã îIDATOÎ[!éIDAT/UšņIDATŧęī‚OIDATĄ‰éî–IDAT3—čūIDATŋsæĶõIDATæaQ:ĨIDATB°ę]TIDATūr=ĸķIDAT^¤ëIENDŽB`‚libspng-0.7.4/tests/images/pp0n2c16.png000066400000000000000000000017021442617636000175570ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_ˆPLTE3f™Ė˙3333f3™3Ė3˙ff3fff™fĖf˙™™3™f™™™Ė™˙ĖĖ3ĖfĖ™ĖĖĖ˙˙˙3˙f˙™˙Ė˙˙3333f3™3Ė3˙3333333f33™33Ė33˙3f3f33ff3f™3fĖ3f˙3™3™33™f3™™3™Ė3™˙3Ė3Ė33Ėf3Ė™3ĖĖ3Ė˙3˙3˙33˙f3˙™3˙Ė3˙˙ff3fff™fĖf˙f3f33f3ff3™f3Ėf3˙ffff3fffff™ffĖff˙f™f™3f™ff™™f™Ėf™˙fĖfĖ3fĖffĖ™fĖĖfĖ˙f˙f˙3f˙ff˙™f˙Ėf˙˙™™3™f™™™Ė™˙™3™33™3f™3™™3Ė™3˙™f™f3™ff™f™™fĖ™f˙™™™™3™™f™™™™™Ė™™˙™Ė™Ė3™Ėf™Ė™™ĖĖ™Ė˙™˙™˙3™˙f™˙™™˙Ė™˙˙ĖĖ3ĖfĖ™ĖĖĖ˙Ė3Ė33Ė3fĖ3™Ė3ĖĖ3˙ĖfĖf3ĖffĖf™ĖfĖĖf˙˙˙3Ė™fĖ™™Ė™ĖĖ™˙ĖĖĖĖ3ĖĖfĖĖ™ĖĖĖĖĖ˙Ė˙Ė˙3Ė˙fĖ˙™Ė˙ĖĖ˙˙˙˙3˙f˙™˙Ė˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙c°ģQåIDATxœÕ–Á ƒ0D§āAË~ˇũ­ö–’†&f“Ėz†Eä=Ö B>/āšīdrs~='3_ÎâķZwˇëÎt(pĸ3—ķíōp]Ņ`힁_čėōytϰĀ?tĻä(Cį\l5čė2õčLģčĖŠg  ķI°@g.`‡Î(`øĮîļgį ôDgī&ĐŊ›Ā(ô(`÷‹Îû60ŊY`zŗĀlô(P4Đ9¯Ü€:{ą€zą€*zȟmtÎ3đ€ÎžøAOŧĄ3ŋæBū^ģÄėIENDŽB`‚libspng-0.7.4/tests/images/pp0n6a08.png000066400000000000000000000014621442617636000175650ustar00rootroot00000000000000‰PNG  IHDR szzôgAMA† 1č–_ˆPLTE3f™Ė˙3333f3™3Ė3˙ff3fff™fĖf˙™™3™f™™™Ė™˙ĖĖ3ĖfĖ™ĖĖĖ˙˙˙3˙f˙™˙Ė˙˙3333f3™3Ė3˙3333333f33™33Ė33˙3f3f33ff3f™3fĖ3f˙3™3™33™f3™™3™Ė3™˙3Ė3Ė33Ėf3Ė™3ĖĖ3Ė˙3˙3˙33˙f3˙™3˙Ė3˙˙ff3fff™fĖf˙f3f33f3ff3™f3Ėf3˙ffff3fffff™ffĖff˙f™f™3f™ff™™f™Ėf™˙fĖfĖ3fĖffĖ™fĖĖfĖ˙f˙f˙3f˙ff˙™f˙Ėf˙˙™™3™f™™™Ė™˙™3™33™3f™3™™3Ė™3˙™f™f3™ff™f™™fĖ™f˙™™™™3™™f™™™™™Ė™™˙™Ė™Ė3™Ėf™Ė™™ĖĖ™Ė˙™˙™˙3™˙f™˙™™˙Ė™˙˙ĖĖ3ĖfĖ™ĖĖĖ˙Ė3Ė33Ė3fĖ3™Ė3ĖĖ3˙ĖfĖf3ĖffĖf™ĖfĖĖf˙˙˙3Ė™fĖ™™Ė™ĖĖ™˙ĖĖĖĖ3ĖĖfĖĖ™ĖĖĖĖĖ˙Ė˙Ė˙3Ė˙fĖ˙™Ė˙ĖĖ˙˙˙˙3˙f˙™˙Ė˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙c°ģQUIDATxœíĐ1 Ā0 CQ<$÷ŋ oĸ ÎÔĨKAË4 zčą-Éë•ũq˙õŗKjIŽČõĢÂ`Á~Ux\€Ku7ĸ€ÃöUIENDŽB`‚libspng-0.7.4/tests/images/ps1n0g08.png000066400000000000000000000026601442617636000175720ustar00rootroot00000000000000‰PNG  IHDR V%(gAMA† 1č–_sPLTsix-cube˙3˙f˙™˙Ė˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙33˙333˙33f˙33™˙33Ė˙33˙˙3f˙3f3˙3ff˙3f™˙3fĖ˙3f˙˙3™˙3™3˙3™f˙3™™˙3™Ė˙3™˙˙3Ė˙3Ė3˙3Ėf˙3Ė™˙3ĖĖ˙3Ė˙˙3˙˙3˙3˙3˙f˙3˙™˙3˙Ė˙3˙˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙f3˙f33˙f3f˙f3™˙f3Ė˙f3˙˙ff˙ff3˙fff˙ff™˙ffĖ˙ff˙˙f™˙f™3˙f™f˙f™™˙f™Ė˙f™˙˙fĖ˙fĖ3˙fĖf˙fĖ™˙fĖĖ˙fĖ˙˙f˙˙f˙3˙f˙f˙f˙™˙f˙Ė˙f˙˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙™3˙™33˙™3f˙™3™˙™3Ė˙™3˙˙™f˙™f3˙™ff˙™f™˙™fĖ˙™f˙˙™™˙™™3˙™™f˙™™™˙™™Ė˙™™˙˙™Ė˙™Ė3˙™Ėf˙™Ė™˙™ĖĖ˙™Ė˙˙™˙˙™˙3˙™˙f˙™˙™˙™˙Ė˙™˙˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙Ė3˙Ė33˙Ė3f˙Ė3™˙Ė3Ė˙Ė3˙˙Ėf˙Ėf3˙Ėff˙Ėf™˙ĖfĖ˙Ėf˙˙Ė™˙Ė™3˙Ė™f˙Ė™™˙Ė™Ė˙Ė™˙˙ĖĖ˙ĖĖ3˙ĖĖf˙ĖĖ™˙ĖĖĖ˙ĖĖ˙˙Ė˙˙Ė˙3˙Ė˙f˙Ė˙™˙Ė˙Ė˙Ė˙˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙˙3˙˙33˙˙3f˙˙3™˙˙3Ė˙˙3˙˙˙f˙˙f3˙˙ff˙˙f™˙˙fĖ˙˙f˙˙˙™˙˙™3˙˙™f˙˙™™˙˙™Ė˙˙™˙˙˙Ė˙˙Ė3˙˙Ėf˙˙Ė™˙˙ĖĖ˙˙Ė˙˙˙˙˙˙˙3˙˙˙f˙˙˙™˙˙˙Ė˙˙˙˙˙"˙h.AIDATxœcd`$Čŗ Œ)ø÷?`y00ʐgdĸy\ ŒđĘū˙Īø‡æq10Ęā•edPåūq5âØYIENDŽB`‚libspng-0.7.4/tests/images/ps1n2c16.png000066400000000000000000000031241442617636000175630ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_sPLTsix-cube˙3˙f˙™˙Ė˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙33˙333˙33f˙33™˙33Ė˙33˙˙3f˙3f3˙3ff˙3f™˙3fĖ˙3f˙˙3™˙3™3˙3™f˙3™™˙3™Ė˙3™˙˙3Ė˙3Ė3˙3Ėf˙3Ė™˙3ĖĖ˙3Ė˙˙3˙˙3˙3˙3˙f˙3˙™˙3˙Ė˙3˙˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙f3˙f33˙f3f˙f3™˙f3Ė˙f3˙˙ff˙ff3˙fff˙ff™˙ffĖ˙ff˙˙f™˙f™3˙f™f˙f™™˙f™Ė˙f™˙˙fĖ˙fĖ3˙fĖf˙fĖ™˙fĖĖ˙fĖ˙˙f˙˙f˙3˙f˙f˙f˙™˙f˙Ė˙f˙˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙™3˙™33˙™3f˙™3™˙™3Ė˙™3˙˙™f˙™f3˙™ff˙™f™˙™fĖ˙™f˙˙™™˙™™3˙™™f˙™™™˙™™Ė˙™™˙˙™Ė˙™Ė3˙™Ėf˙™Ė™˙™ĖĖ˙™Ė˙˙™˙˙™˙3˙™˙f˙™˙™˙™˙Ė˙™˙˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙Ė3˙Ė33˙Ė3f˙Ė3™˙Ė3Ė˙Ė3˙˙Ėf˙Ėf3˙Ėff˙Ėf™˙ĖfĖ˙Ėf˙˙Ė™˙Ė™3˙Ė™f˙Ė™™˙Ė™Ė˙Ė™˙˙ĖĖ˙ĖĖ3˙ĖĖf˙ĖĖ™˙ĖĖĖ˙ĖĖ˙˙Ė˙˙Ė˙3˙Ė˙f˙Ė˙™˙Ė˙Ė˙Ė˙˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙˙3˙˙33˙˙3f˙˙3™˙˙3Ė˙˙3˙˙˙f˙˙f3˙˙ff˙˙f™˙˙fĖ˙˙f˙˙˙™˙˙™3˙˙™f˙˙™™˙˙™Ė˙˙™˙˙˙Ė˙˙Ė3˙˙Ėf˙˙Ė™˙˙ĖĖ˙˙Ė˙˙˙˙˙˙˙3˙˙˙f˙˙˙™˙˙˙Ė˙˙˙˙˙"˙h.åIDATxœÕ–Á ƒ0D§āAË~ˇũ­ö–’†&f“Ėz†Eä=Ö B>/āšīdrs~='3_ÎâķZwˇëÎt(pĸ3—ķíōp]Ņ`힁_čėōytϰĀ?tĻä(Cį\l5čė2õčLģčĖŠg  ķI°@g.`‡Î(`øĮîļgį ôDgī&ĐŊ›Ā(ô(`÷‹Îû60ŊY`zŗĀlô(P4Đ9¯Ü€:{ą€zą€*zȟmtÎ3đ€ÎžøAOŧĄ3ŋæBū^ģÄėIENDŽB`‚libspng-0.7.4/tests/images/ps2n0g08.png000066400000000000000000000044201442617636000175670ustar00rootroot00000000000000‰PNG  IHDR V%(gAMA† 1č–_zsPLTsix-cube˙3˙f˙™˙Ė˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙33˙333˙33f˙33™˙33Ė˙33˙˙3f˙3f3˙3ff˙3f™˙3fĖ˙3f˙˙3™˙3™3˙3™f˙3™™˙3™Ė˙3™˙˙3Ė˙3Ė3˙3Ėf˙3Ė™˙3ĖĖ˙3Ė˙˙3˙˙3˙3˙3˙f˙3˙™˙3˙Ė˙3˙˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙f3˙f33˙f3f˙f3™˙f3Ė˙f3˙˙ff˙ff3˙fff˙ff™˙ffĖ˙ff˙˙f™˙f™3˙f™f˙f™™˙f™Ė˙f™˙˙fĖ˙fĖ3˙fĖf˙fĖ™˙fĖĖ˙fĖ˙˙f˙˙f˙3˙f˙f˙f˙™˙f˙Ė˙f˙˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙™3˙™33˙™3f˙™3™˙™3Ė˙™3˙˙™f˙™f3˙™ff˙™f™˙™fĖ˙™f˙˙™™˙™™3˙™™f˙™™™˙™™Ė˙™™˙˙™Ė˙™Ė3˙™Ėf˙™Ė™˙™ĖĖ˙™Ė˙˙™˙˙™˙3˙™˙f˙™˙™˙™˙Ė˙™˙˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙Ė3˙Ė33˙Ė3f˙Ė3™˙Ė3Ė˙Ė3˙˙Ėf˙Ėf3˙Ėff˙Ėf™˙ĖfĖ˙Ėf˙˙Ė™˙Ė™3˙Ė™f˙Ė™™˙Ė™Ė˙Ė™˙˙ĖĖ˙ĖĖ3˙ĖĖf˙ĖĖ™˙ĖĖĖ˙ĖĖ˙˙Ė˙˙Ė˙3˙Ė˙f˙Ė˙™˙Ė˙Ė˙Ė˙˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙˙3˙˙33˙˙3f˙˙3™˙˙3Ė˙˙3˙˙˙f˙˙f3˙˙ff˙˙f™˙˙fĖ˙˙f˙˙˙™˙˙™3˙˙™f˙˙™™˙˙™Ė˙˙™˙˙˙Ė˙˙Ė3˙˙Ėf˙˙Ė™˙˙ĖĖ˙˙Ė˙˙˙˙˙˙˙3˙˙˙f˙˙˙™˙˙˙Ė˙˙˙˙˙–Đ‹†AIDATxœcd`$Čŗ Œ)ø÷?`y00ʐgdĸy\ ŒđĘū˙Īø‡æq10Ęā•edPåūq5âØYIENDŽB`‚libspng-0.7.4/tests/images/ps2n2c16.png000066400000000000000000000046641442617636000175760ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_zsPLTsix-cube˙3˙f˙™˙Ė˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙3˙33˙3f˙3™˙3Ė˙3˙˙33˙333˙33f˙33™˙33Ė˙33˙˙3f˙3f3˙3ff˙3f™˙3fĖ˙3f˙˙3™˙3™3˙3™f˙3™™˙3™Ė˙3™˙˙3Ė˙3Ė3˙3Ėf˙3Ė™˙3ĖĖ˙3Ė˙˙3˙˙3˙3˙3˙f˙3˙™˙3˙Ė˙3˙˙˙f˙f3˙ff˙f™˙fĖ˙f˙˙f3˙f33˙f3f˙f3™˙f3Ė˙f3˙˙ff˙ff3˙fff˙ff™˙ffĖ˙ff˙˙f™˙f™3˙f™f˙f™™˙f™Ė˙f™˙˙fĖ˙fĖ3˙fĖf˙fĖ™˙fĖĖ˙fĖ˙˙f˙˙f˙3˙f˙f˙f˙™˙f˙Ė˙f˙˙˙™˙™3˙™f˙™™˙™Ė˙™˙˙™3˙™33˙™3f˙™3™˙™3Ė˙™3˙˙™f˙™f3˙™ff˙™f™˙™fĖ˙™f˙˙™™˙™™3˙™™f˙™™™˙™™Ė˙™™˙˙™Ė˙™Ė3˙™Ėf˙™Ė™˙™ĖĖ˙™Ė˙˙™˙˙™˙3˙™˙f˙™˙™˙™˙Ė˙™˙˙˙Ė˙Ė3˙Ėf˙Ė™˙ĖĖ˙Ė˙˙Ė3˙Ė33˙Ė3f˙Ė3™˙Ė3Ė˙Ė3˙˙Ėf˙Ėf3˙Ėff˙Ėf™˙ĖfĖ˙Ėf˙˙Ė™˙Ė™3˙Ė™f˙Ė™™˙Ė™Ė˙Ė™˙˙ĖĖ˙ĖĖ3˙ĖĖf˙ĖĖ™˙ĖĖĖ˙ĖĖ˙˙Ė˙˙Ė˙3˙Ė˙f˙Ė˙™˙Ė˙Ė˙Ė˙˙˙˙˙˙3˙˙f˙˙™˙˙Ė˙˙˙˙˙3˙˙33˙˙3f˙˙3™˙˙3Ė˙˙3˙˙˙f˙˙f3˙˙ff˙˙f™˙˙fĖ˙˙f˙˙˙™˙˙™3˙˙™f˙˙™™˙˙™Ė˙˙™˙˙˙Ė˙˙Ė3˙˙Ėf˙˙Ė™˙˙ĖĖ˙˙Ė˙˙˙˙˙˙˙3˙˙˙f˙˙˙™˙˙˙Ė˙˙˙˙˙–Đ‹†åIDATxœÕ–Á ƒ0D§āAË~ˇũ­ö–’†&f“Ėz†Eä=Ö B>/āšīdrs~='3_ÎâķZwˇëÎt(pĸ3—ķíōp]Ņ`힁_čėōytϰĀ?tĻä(Cį\l5čė2õčLģčĖŠg  ķI°@g.`‡Î(`øĮîļgį ôDgī&ĐŊ›Ā(ô(`÷‹Îû60ŊY`zŗĀlô(P4Đ9¯Ü€:{ą€zą€*zȟmtÎ3đ€ÎžøAOŧĄ3ŋæBū^ģÄėIENDŽB`‚libspng-0.7.4/tests/images/s01i3p01.png000066400000000000000000000001611442617636000174640ustar00rootroot00000000000000‰PNG  IHDRRÜf\gAMA† 1č–_sBITwøĩŖPLTE˙ŠxŌW IDATxœc`H¯¤qIENDŽB`‚libspng-0.7.4/tests/images/s01n3p01.png000066400000000000000000000001611442617636000174710ustar00rootroot00000000000000‰PNG  IHDR%ÛVĘgAMA† 1č–_sBITwøĩŖPLTE˙ŠxŌW IDATxœc`H¯¤qIENDŽB`‚libspng-0.7.4/tests/images/s02i3p01.png000066400000000000000000000001621442617636000174660ustar00rootroot00000000000000‰PNG  IHDR?¯ņgAMA† 1č–_sBITwøĩŖPLTE˙˙\/% IDATxœc`ūŒgČIENDŽB`‚libspng-0.7.4/tests/images/s02n3p01.png000066400000000000000000000001631442617636000174740ustar00rootroot00000000000000‰PNG  IHDRHxŸggAMA† 1č–_sBITwøĩŖPLTE˙˙\/% IDATxœc```ö8UIENDŽB`‚libspng-0.7.4/tests/images/s03i3p01.png000066400000000000000000000001661442617636000174730ustar00rootroot00000000000000‰PNG  IHDRájgAMA† 1č–_sBITwøĩŖPLTE˙˙wąåĨŸ IDATxœc`€LAÚÉæIENDŽB`‚libspng-0.7.4/tests/images/s03n3p01.png000066400000000000000000000001701442617636000174730ustar00rootroot00000000000000‰PNG  IHDRlæ'ügAMA† 1č–_sBITwøĩŖPLTE˙˙wąåĨŸIDATxœc``p``ÆA91 KIENDŽB`‚libspng-0.7.4/tests/images/s04i3p01.png000066400000000000000000000001761442617636000174750ustar00rootroot00000000000000‰PNG  IHDRä8<ĢgAMA† 1č–_sBITwøĩŖPLTE˙w˙˙š÷CšIDATxœch`Á  >Žšß7öIENDŽB`‚libspng-0.7.4/tests/images/s04n3p01.png000066400000000000000000000001711442617636000174750ustar00rootroot00000000000000‰PNG  IHDR“? =gAMA† 1č–_sBITwøĩŖPLTE˙w˙˙š÷CšIDATxœcøĀ0? ‚ˇļ IENDŽB`‚libspng-0.7.4/tests/images/s05i3p02.png000066400000000000000000000002061442617636000174710ustar00rootroot00000000000000‰PNG  IHDR‡ūāgAMA† 1č–_sBITwøĩŖ PLTE˙˙w˙˙A¤ĒsIDATxœch`h`XĀ";€tOm"Mƒãp$FEIENDŽB`‚libspng-0.7.4/tests/images/s05n3p02.png000066400000000000000000000002011442617636000174710ustar00rootroot00000000000000‰PNG  IHDRđÎvgAMA† 1č–_sBITwøĩŖ PLTE˙˙w˙˙A¤ĒsIDATxœcXÕĀ0ĩa"˜\Õ*ŽŒôJIENDŽB`‚libspng-0.7.4/tests/images/s06i3p02.png000066400000000000000000000002171442617636000174740ustar00rootroot00000000000000‰PNG  IHDRęĨ7MgAMA† 1č–_sBITwøĩŖ PLTE˙w˙˙˙ŖEh"IDATxœch`h`˜Ä - +<"Ļ&0LH`XĩYĮLČeí$IENDŽB`‚libspng-0.7.4/tests/images/s06n3p02.png000066400000000000000000000002031442617636000174740ustar00rootroot00000000000000‰PNG  IHDRĸÛgAMA† 1č–_sBITwøĩŖ PLTE˙w˙˙˙ŖEhIDATxœcXĩ€ajÃ02V-9Ī_)pũĘIENDŽB`‚libspng-0.7.4/tests/images/s07i3p02.png000066400000000000000000000002251442617636000174740ustar00rootroot00000000000000‰PNG  IHDRÎ;ÖgAMA† 1č–_sBITwøĩŖ PLTE˙w˙w˙˙˙Cŗœ%IDATxœc8Āp€áÆ ¯ū3üaHÂ? WcnĖ’Á㠁U‚ų‰IENDŽB`‚libspng-0.7.4/tests/images/s07n3p02.png000066400000000000000000000002121442617636000174750ustar00rootroot00000000000000‰PNG  IHDRš<ŋ@gAMA† 1č–_sBITwøĩŖ PLTE˙w˙w˙˙˙CŗœIDATxœcø˙‡áj í9 7æ€H û˙qy š]mIENDŽB`‚libspng-0.7.4/tests/images/s08i3p02.png000066400000000000000000000002251442617636000174750ustar00rootroot00000000000000‰PNG  IHDRÎffŽgAMA† 1č–_sBITwøĩŖ PLTE˙˙w˙w˙˙ĒēY%IDATxœc` ~Ā`Į ¤÷0ĖaXÁ ĩ‚Aˇ‚A˙PP×÷pĶJ¯IENDŽB`‚libspng-0.7.4/tests/images/s08n3p02.png000066400000000000000000000002131442617636000174770ustar00rootroot00000000000000‰PNG  IHDRšaVgAMA† 1č–_sBITwøĩŖ PLTE˙˙w˙w˙˙ĒēYIDATxœc```ĐZÁ ˙ƒAˇ„€ —=V=ųLIENDŽB`‚libspng-0.7.4/tests/images/s09i3p02.png000066400000000000000000000002231442617636000174740ustar00rootroot00000000000000‰PNG  IHDR ęøŪgAMA† 1č–_sBITwøĩŖ PLTE˙w˙˙˙˙w˙Vd#IDATxœc`@ €X Œ^3\bû˙ –Ņ du˙FŒ—IENDŽB`‚libspng-0.7.4/tests/images/s09n3p02.png000066400000000000000000000002171442617636000175040ustar00rootroot00000000000000‰PNG  IHDR ˙îƒgAMA† 1č–_sBITwøĩŖ PLTE˙w˙˙˙˙w˙VdIDATxœc`û˙ VĢ,ŖXBƒø q ieÃjC#IENDŽB`‚libspng-0.7.4/tests/images/s32i3p04.png000066400000000000000000000005431442617636000174770ustar00rootroot00000000000000‰PNG  IHDR öSWQgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ØIDATxœ}Ŋ‚0F?E~âS¸׆Š$đFē:5qĢ,넁™Š3‰$đXļôV)16͗Ú÷æ6‚ãÉđäH^āúöæŠé|Gč¸O6āvjP›h–ąÕq¨ ÕöS˛XĻŖL!0ZéĮÄÎčė÷ĖÂ(ˆ-•`ÎFU…Ē Ę‚ $čāLéz*‚Č>=& „r%•ž@=EįĻ"‚tŽ6;sâŅ2 ž¨qô2ėüĸėāwč­Ãz„ÄE×ÂĨ0ŒĨ¸ūl%ôĢ=!°Ū˛Z;˙‰7„RŌ„”íÄ#IENDŽB`‚libspng-0.7.4/tests/images/s32n3p04.png000066400000000000000000000004071442617636000175030ustar00rootroot00000000000000‰PNG  IHDR TgĮgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;|IDATxœ•ŅŊ € āW‰q zZļō6 w +'a0MŒīHˆR\ČGríČväX¯ˆ4Ā1$Tx†‘As蒿Ŗ?“´hnÜØs­ĸoĢ‚f]A[Öä $ \VÚ~01ØYB6ŗ˜iīˇpaÃHëz&.IENDŽB`‚libspng-0.7.4/tests/images/s33i3p04.png000066400000000000000000000006011442617636000174730ustar00rootroot00000000000000‰PNG  IHDR!!ŌÍīĘgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;öIDATxœ]1rÂ0E?É0(ÄÜĀ“xFHÁܸ§ĸĻSKŠ’–Îĩ+ú4>€•ŋĢ–y˒žVo Äæ4Ãû‘ķÄš‹üp‡ā˛ķ:—ã4Ëa†`5”Ũ$̘9¤ák Lã~F´øQZ„° Â#Jš3^ސŗŨ“K?ZÉ]Tã4Zč?Ŋō°÷&ikEÄĢJ•ë ú3¤) ũŗ]Ša—VÔYĘEh ü-įîøoÄģÍĮ$ī`û*ķ1ásÚđ¸ŊPŠxũKEĩ„= "šĻaXID›ķŒ›o-MÜBCķ:ĩe֖)Z™ir450NšēR“k[]IČŅÔldnaȕGRŋ[_‘éōŲ{Qŗ _Eĸ˙6bĀp&], īP—]IÍ4ŧ?tũ3 ĻēPÚÅ]jP33/2‡˙ ˙ņ˙îZ߈)IENDŽB`‚libspng-0.7.4/tests/images/s34n3p04.png000066400000000000000000000003701442617636000175040ustar00rootroot00000000000000‰PNG  IHDR""ČiņgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;mIDATxœ•Īą € Đ_ ‰[ØĐÛ2–Đŗ“0§&˙GÁ+.ä%üģÎ:rĄŨ*ÖáHv4,$Ä‚Y49n2=ü¨xNNļmøŧ'c)*+ TxVRņ,v5Hę„\õ-Y~ŊHŦÅíˆ:+ĄNIENDŽB`‚libspng-0.7.4/tests/images/s35i3p04.png000066400000000000000000000006171442617636000175040ustar00rootroot00000000000000‰PNG  IHDR##›đžügAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;IDATxœe=Ž‚P„ĮøQ‹÷v@XÉŲ€… °ąˇ˛ĻŖĩ¤´ĩŖĻ˛ ¸‹zsÎå‡đ“™;ų&Í}č!rBÃī€ŧŅ— ũ8‰†ycže¤c:1 4æˆ;0'Ô9]ģīL(PFĄŽŗÚ„ž–å”Ē*Ļ1j1-†´Ft­Ÿ”ĩHÜã´Ą?†¸×š›ŪÅHņîDŗ"nÆŽŲMMŊ;-…wƒw+T0ũÍôÁãqį=ĶīŪ‰^¸ņÚŪfēâBŲÃuÆ/e×p cAä†%q)uMa›‘8ÂVFL`?Ha;đ L‰[ŖÎ‰qūЊiIENDŽB`‚libspng-0.7.4/tests/images/s35n3p04.png000066400000000000000000000005221442617636000175040ustar00rootroot00000000000000‰PNG  IHDR##ė÷ŽjgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ĮIDATxœeŅ!ƒ@…á×´”Ļ˜ö„p €ÁŖĐ¸ÚĘJ,Ē¯á{¨>Ļa3ŗûFŦ˜ė—ļŪĒ—œāĢú`Y\ՌžīøĢ:TŽ•ŽÄ4b2(¸ŊĐåhų%­ĒsnŨ†oEÍdė=đd2öܙŒŊ›øBcF•5ÎŧWhäQd *āßFŖáą&āąÕōNŪčyŠ7zŪAŒ†wŖáe\/ī¨x’å7rLŒŒ#"ckLÄiÎqŊ6ÖÛŦ‘>¤ÃâîGÎĸåHIENDŽB`‚libspng-0.7.4/tests/images/s36i3p04.png000066400000000000000000000005441442617636000175040ustar00rootroot00000000000000‰PNG  IHDR$$d)ĩ=gAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ŲIDATxœ‘Ŋ ƒ0F?Ûú͎č"]ÅIÁĄžQģv ¸iwqpvrTĐĮjbMĸI †äãxîŊAˆ(;¯ÕņۈV›LIFqôxfSB0'ž×æŽzÎæWÖsž‰%ķĀÍÑmŒ åĄrŽtã†Č \AĄp9ø˛ú*e}™ ˛uŽ”}• GT_õ—`-ÔU$ĒĒÚĒz3‰õÍëQ´ą?,‰ĸ 0hdž¯+Ä8郹oÜe(8ņEībī _ŋzØŠjūļ ,a*mģuuöŠĻZū9?„.ûÂIENDŽB`‚libspng-0.7.4/tests/images/s36n3p04.png000066400000000000000000000004021442617636000175020ustar00rootroot00000000000000‰PNG  IHDR$$.…ĢgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;wIDATxœc`Âîe–ĒG $! ÅĀ‚.dĀp]ˆ]hƒ*ēĐt !,Æ[*`:B•,!,BlÆ7Ũ…î,NEâPWč¨B €BŒ¨B œĒЄ€$ĄBÂB62`ą‘XÉÂôoƒIČĶ‘ŦžIENDŽB`‚libspng-0.7.4/tests/images/s37i3p04.png000066400000000000000000000006111442617636000175000ustar00rootroot00000000000000‰PNG  IHDR%%@ˇ ĻgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ūIDATxœe;‚0†˙ņQč NĀL.`áhė­¨éhSRŌÚĨϞˇÉr(7ō0ų’Ė˛Ëˇ`’ĐFCˆjŪ} |nēž(`,™,°›äœ–ŋjŽKÎ)#ˆ)ą¨ãÂ(ÜĐ8nĮũČ P M“$0 –6!a ?ؑ\Ŗ‹Zs!*áˆV}6n¸ §J_DžØģ”ĩÖõ w9čûŊ)/ŧgŧ˙™ņ¤g÷Léđ x Üq%x \€‰r/ĩRx Č[ĐåJæšsÉFĩNk-Ŋ8Ūš—FߎķĻRz'š#ą?ŗIENDŽB`‚libspng-0.7.4/tests/images/s37n3p04.png000066400000000000000000000005201442617636000175040ustar00rootroot00000000000000‰PNG  IHDR%%7°=0gAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ÅIDATxœeŅ-ƒ@†á/ĄMú'ā '؄ z …ŽĢ­ŦÄâV¯ĒĮpÕŲ)Ĩŗ3Ų›x3@ô’ž|…Iz#„€S<úžŖ#u¨*dUÂa0Ļ”ô‰2qEKĪž•,Ë2Įņ7ãFxlj<ÍáąÉšÕôž)Põzú?ĶKwļWs:öÛkP¯Ņ¤ŠĸW¤2•Ę -WĢ]s'Ū ŧjĩļ×eļGÛ ÛÛĀöÎŨKĢŪ­ûđÛ¯ŒGÅč|ú…QV ŊIENDŽB`‚libspng-0.7.4/tests/images/s38i3p04.png000066400000000000000000000005451442617636000175070ustar00rootroot00000000000000‰PNG  IHDR&&-Ä gAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ÚIDATxœĨ‘? ‚@†ŸūXA_ĸEZĨЃ†üFĩ6¸iƒ‹ģ4475 úą:EīĪ@Hî÷ōøđŪŠĮŠænÍæ#9öSЎŦOQÍbĶdTw)‹–Ûūq!oķņ;s+;q°Ž•ZIÕc|°ėÚØj€įÆb[˜ŊŗP­ī4Ôči|alj癯•AŦn6 S4zg4†ŒXŦn8Öm íuMžÂ/û$y”C—ã;ÁŌŲ+|÷<׹;§§^Åy†}÷—kūävāšob‚Ũ땗){ĮzB—|õDyüEIENDŽB`‚libspng-0.7.4/tests/images/s38n3p04.png000066400000000000000000000003651442617636000175140ustar00rootroot00000000000000‰PNG  IHDR&&ZôgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;jIDATxœc`Âîe–ĒG`$61 ÍĀ‚!fĀpCŒClƒ*†Đ L1lvX*`q‹*šb ØÄØ0íhēÃ-ØÜŒĖ81 ËĐÅ €bÜhb)`@’d‹a1›ŊƒÉ-ā°ōi+ƒÎŦDáIENDŽB`‚libspng-0.7.4/tests/images/s39i3p04.png000066400000000000000000000006441442617636000175100ustar00rootroot00000000000000‰PNG  IHDR'' Š|gAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;IDATxœm1rƒ0EŸĮ““Î NŒ."pãžĘĩ;ZJ•nŨQSš§á:TVå1Āđĩģ˙/ {f3ŖTƒÖ` ĸ™D›i”@QhŒåų–j÷ZjüŠvŠJ•ž°JĶ8Å5õ=ãPŽ*ęēîŖķ0 ęzS`9Æ!T5kÁ=Œ”Øí´- K#ę¨<͚ĄÂĖ_.ēŦãÜĨ?N€u‚ŠbÆĘo&Ä8:ë/G„Ŗ"L,ÄūÍB„ čM‚Į”đâvkåNhy>xîxp•ëíšpáWp•ž÷X9ç¸eî’(wĪ÷œËÎxãž:Ιņ4eÎíģ5ßš?NÖ|īîåæûĩ.@ęn­yũ –5ö ūâāIENDŽB`‚libspng-0.7.4/tests/images/s39n3p04.png000066400000000000000000000005401442617636000175100ustar00rootroot00000000000000‰PNG  IHDR''~LgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ÕIDATxœmŌ!ƒ@…áGH›”zƒ†p €ÁŖĐ8,‰ÅĄWá1€CufICgf`Äø2Ü(ü6Ņ į'ZĐļ =ĸE¸å˜'ĖĒ Ŋ'“ŊQĶuĢEŽãØyüĩŖ¤ü¸ú §ü¸ęđĸü¸JŊÛڟdÕö…žĶÚé0`7å~ÚmdˇUŠũg/Cö>d—î(Uė‘>öļŅąb'LWîæÎteĪcĻ+ûô`:4›nhûų7čŅözåŅoíbåž/í ĮĒė O–/Ļ5+˙ŸæIENDŽB`‚libspng-0.7.4/tests/images/s40i3p04.png000066400000000000000000000005451442617636000175000ustar00rootroot00000000000000‰PNG  IHDR(( וČgAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;ÚIDATxœ­Žą ƒ0E¯ÅZkû]¤ĢdRčP˙¨ŽN7upqg'įB úYÕP1¯…BCŪÍáŧ<äūĮąrQ§ũāđeAœ|¸$`O™ ëŦãsc#‹ŦWéLYŨDâÍéĻO™ØāLĸãILČž ˇã{–čļaWŠvĨD(´ cÆNŲŽüaÍX„ đĒ­DCa¨pl–5pŸQÖ‹DVí ‰@—Ü"$šēœ–Ö“ēųI#ũUžÆ­ĩœ&!AHt ƒxnR=͈kŌŋi^ jNpķ{xIENDŽB`‚libspng-0.7.4/tests/images/s40n3p04.png000066400000000000000000000004001442617636000174730ustar00rootroot00000000000000‰PNG  IHDR((~ĐĨ^gAMA† 1č–_sBITwøĩŖ'PLTE˙w˙˙˙w˙w˙w˙˙˙˙˙w˙˙˙w˙õŋ;uIDATxœÍĐą €0 DŅCŠ,!– aˆlE6HŸ%¨2ƒ¨ūEčHáâÉVN§RĩŠÔø˜bÜG@!FŅeQk7^¯ Bęķ÷ؚp87´7*¤,‡mâĻWBMwÁG)R✏éÃc!wu'įgWpH])IENDŽB`‚libspng-0.7.4/tests/images/tbbn0g04.png000066400000000000000000000006551442617636000176340ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_tRNSæ,ĐŠbKGDĒ#2HIDAT(‘uŅ1OÂ@Āq>‚GYL\Ž$.&&…Ē“K[,åB˜î&Žô`rĸ“Â%úŨ$.˛2˜°š8°jŅę]mHxÛũĻwīŸû^™Ü:x™-ArO+ˇ đŅ’YŦ3HžÂjšdcs˙&…ĪAč–m×v‹{ŦũŦ`ěeģVõ,§œ\Ŧ´MĻëę˙ã’VĪ[*M÷IENDŽB`‚libspng-0.7.4/tests/images/tbbn2c16.png000066400000000000000000000037711442617636000176370ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_tRNS˙˙˙˙˙˙žŊK2bKGD˙˙GeŠ€ŒIDATX…ÕØ Leđī”G0øˆnL$QŅØÄbS˜bĀ–ĄĩŖe#Ąv<\" L T”ÂēŌRZ e7ˇ9ĸN4:qd:f 1fĖDãsļšŠņũ 6YPēAŒŋ„ËåŽŪ÷˙ī;ņû˙œø¯¸X ’Āįä9Mĸ6 |Mö’:R@JJuuw€­[ĶĀØ˜ ;6.ž\r'É(ą‘"b2åęęd°qãÕĀá’VĢUUxlhĀcg§z{Áč¨LMM€_Č<'p†ŧCŧ¤Œ<ņ„ TU=ššn^/†ÕŨAˇļb Ī<ƒįeeŅĀ`Āp5š+@v6ŪÕë˙NĪíŽÕՅ` 9D~$!$đ'¤’”“ÚZŦãĻ&ėO$0ˆmÛ0ˆ]ģđxāÂãČŪííÅ̛¯VëmĀbšÔ×ãŨövüĪ–<šLPB¸D.ŨO¸šũgMā ąūņSAÚۓĀ+¯`Á/ŋŒE áųŽXĮmm÷ˇ;ŒŒÄÉÉKĀgŸá˙LOã˙õ?ųd&I¸ūęĢxn6c’ÜĒOÍb-áj5#ÄJ6RCŦÖGĀđ0Ößļm7‡cXŋ ¨:O}=48x˜œŒĮ_žųž˜ĀqŌß ¨ŠŅķŸPJV“‡É>2kßēž`"VĢ“đåöyzÁpËÉ#$›d‘/ÉŦ đ ąX,–øøŽŽøø°°ūū°°ääžžädģŊ­Ín÷ōŠš ÕA8tî$9$8hŅ“Äl#‘¤={$čĨīž‹}õĖœ7††ôz7áЃ´‘&Rĸ'I.ČĪĮÎdĩ†;ŲúõxE§KjõŗdN pˆŅŅ/ŧ-Äž}˜ĀŲŗBÄÆNMÅÆļšØDxŽās;áÎVÄL¸?JpŪÉĪĮŅđÆXÎÁƒ<­ÂAzė1-Ē%vû’% ÅæÍ˜ĖT Ŗđx”J^/ƒĶā ö{Ŋ> œ<‰!ž:…sŽÉt7ČÍÅ͉˅O‚- Ü­­Ŋäåq…†/gÔyf6]QQ>_T”GŽ`!BÄÅų|qqBzûmŧ‚ûšÔTˇ;5ÕG8 îNr?°Xūę÷ŌČČõ /k]ĨÂgpE™Lx×dēŦ\y–„okŗŗ÷04Ą@¯WˆÄÄĒĒÄD!Ūz Ųŋʐ<X+$ž‚ĩ—™Ųښ™<Ŋr<|ąķôõáķž˙CllÄŨ•Fƒ÷Î;qū1ņIååx×ī_JKg }ÖXqqyyqą\ EJJAAJĘĸEũũ‹ ņŅGX”ŨŽwGGņÜéB­všÔęāeŽđúŠõŊw/ūâčQ ą¸8df&œsÖ­Ã'íŲÃ-ĐÜî L ™„‡ááBÂͲLVX(“-[fĩ.[&ÄøøLŋ‡pxmnBĨr:U*^%8 ŪtŦ$W‚áaüÅČÎ<……ÛaÍŪ|ã“ēēpœčtûÉ&đqšvíršd2ĢU&3ÖŦ1ŒÆĩkÆˆˆ;#"„xņEgĻ5„Fãph4ŧZsŧԒËĀķĪã/ēēpȍ2°z5eŋ¯īŪ­´|9žžžöÚ?‡ū/ › ëŋī&&ļ´āxxé%,Ū €Í&„Vkˇkĩ\ëœīj9ÅĻH _.qŠ,(Ā#ŧhŌ[Ų’’ĀÄÄ\ĸ !vœ4Ž´T§bįN,œúŦذ[ĀfÃV@œ¯Áŧ1ž¨T8XyžnŒÆ[AEŧā‹/æOČ 0žŖÉĖvCâ×ÉÆF!rrššrrx3ĮiđŦ!J‚$.įø´4ee'Āôt¨‘\`Ážœ<|X.÷ûårždÍæúzŗ™ßæ8 îxü>ĩ‚ÜCxOĘ#íbb˜‡īBĶ„7müŠÃŨŒĶā5ø!¯)üŸŋ’‹/}ŪžĖũFŪ$üVÍiđĖ-0LæĢDļ ŸáY‹ŋw,DY øq÷áī¤ WĘ˙ūëô› ųúĸaIENDŽB`‚libspng-0.7.4/tests/images/tbbn3p08.png000066400000000000000000000027331442617636000176530ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_âPLTE˙˙˙€VVĩĩ¸¨BBŸŸŸą ‹Y›››„™™™§———•••“““))V‘‘‘›‹•‹..§€‹‹‹š‰‰‰ Õxuu‡‡‡˛………Ĩہž}}}É{{{yyy77VwwwuuusssHŠHŽdbV‰V((|S‹S‰‰gggeee]m]å†&&o--D‘Daaa;;D‰D=“=¤ķņYYYWWWUUUSSS4…4QQQ$—$OOO::Aē˛ĮÅüüü×Á-w-úúúŋhŊÚÔÔ{ ­ øøøšˇœœĄööö Ą ŗą‘ĢōōōЧîîîėėė—ęęęk‹‰‡111*@Ž îĶÖĶĖĖĖ“Ŗ**ÆÆÆÄÄÄĖĶ kkx>>mŸ VFFHAAMs]]Q¨¨¨ííī   žžžœœœšššš˛˜˜˜ë–––ž”””’’’ŽŽŽ‘ŠŠŠˆˆˆvĸv…ˆ…†††„„„x‚‚‚~‚~~~~|||zzzJĀJvvvtttrrrppp˜nnnjpjzffjjj„DĸDK–Kadabbbô8˜8\\\ZZZæ]BxBVVVđ.”.GhG11`ØRRRPPPÎ!˜!mČLLLũũũƝokkęHHHŧ4f4õSS`°Žˇ¤īīīĸOO•44˜–’įįįŒãã〒odVYdNNkĪĪĪŨŨā{ÉÉÉA!!YWWYDDxŋŋŋëŨŨ--T `˙ŋ}}ÎaûtRNS@æØfbKGDõEŌÛŠIDAT8c` 2xđ* ė=ĻÅfO,™ŋ|Ā*›ų^Ę´­Moƒø†ÕU_"PeĶjøVži°Ŧœ\)žĀĒŊŊ}ÃŖu]Īc`˛1¯ú–ë´Y>šŦĐ_[+ŠĀfš@|ĩŪ“5:Ë9k2Á *”—kkߒėŋę{ë&ĢĸâĉŠĩ’âg´`}XÁ ųŲķÕ}YoĘhiiÕ3ם=[WÅdŽÚ~č=XAÎ|Ĩš3¤”5PÁō-އO܃¸a‘į5[~áųę0°tŠÆz—Ã'Î9C]9+é”ã˛i|ŧSd—@ÁĘíFAQ- Ą ĻEGlĩœÂËÍÉ-ĄŧxąĒ֖K>j3ÃÃ:  Ļ$&íÜ:‡_dŖ.OwΌÅëwŧ•ŪÛ´ĢĀŖĒ ãhęņ­ËĻéžOßĮÎŌĩt‡ŗčgĩ]ÅS›5aÁɴN"ßŪÖĶͲÂųŲÅŊŪį^ Ŧ„eĩ{rŪĮMļ~ˇí9Ųģ× pxĶÕ43Ļ rwōÃX§ÛSŲö}œėĻoËö6RyųŠĻ g÷•Â㞁wė'ôõØŧûÜËņúen.GLAĀūô;!ÁūwƒīnâåîÛ!z‘ëcąūcĄgˆØ^{íÚūăųay›øx'š”î o-:&—k‚P vãōģãŲįī[ķķ īņéÕo9āpēšĄ ”Į}ŗYô•Ø|cA~)×Ō͟Éq0Ą$ŠįkĖö%Å *šFqésÍ<ö-IfÍ3ŋœT`,&ŧÔõcãSŖ č  ãčåBC ąnŪÅ%ī:1䁠|[ČĸŲkyi…b“g`¸Ū5ŪlSÃ]'ąËÁ{ųy6‡˜pJÁ‹éføäā—Į Ž˙‘•ÛĀIENDŽB`‚libspng-0.7.4/tests/images/tbgn2c16.png000066400000000000000000000037711442617636000176440ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1āgAMA† 1č–_tRNS˙˙˙˙˙˙žŊK2bKGD˙˙™Ū‰cŒIDATX…ÕØ Leđī”G0øˆnL$QŅØÄbS˜bĀ–ĄĩŖe#Ąv<\" L T”ÂēŌRZ e7ˇ9ĸN4:qd:f 1fĖDãsļšŠņũ 6YPēAŒŋ„ËåŽŪ÷˙ī;ņû˙œø¯¸X ’Āįä9Mĸ6 |Mö’:R@JJuuw€­[ĶĀØ˜ ;6.ž\r'É(ą‘"b2åęęd°qãÕĀá’VĢUUxlhĀcg§z{Áč¨LMM€_Č<'p†ŧCŧ¤Œ<ņ„ TU=ššn^/†ÕŨAˇļb Ī<ƒįeeŅĀ`Āp5š+@v6ŪÕë˙NĪíŽÕՅ` 9D~$!$đ'¤’”“ÚZŦãĻ&ėO$0ˆmÛ0ˆ]ģđxāÂãČŪííÅ̛¯VëmĀbšÔ×ãŨövüĪ–<šLPB¸D.ŨO¸šũgMā ąūņSAÚۓĀ+¯`Á/ŋŒE áųŽXĮmm÷ˇ;ŒŒÄÉÉKĀgŸá˙LOã˙õ?ųd&I¸ūęĢxn6c’ÜĒOÍb-áj5#ÄJ6RCŦÖGĀđ0Ößļm7‡cXŋ ¨:O}=48x˜œŒĮ_žųž˜ĀqŌß ¨ŠŅķŸPJV“‡É>2kßēž`"VĢ“đåöyzÁpËÉ#$›d‘/ÉŦ đ ąX,–øøŽŽøø°°ūū°°ääžžädģŊ­Ín÷ōŠš ÕA8tî$9$8hŅ“Äl#‘¤={$čĨīž‹}õĖœ7††ôz7áЃ´‘&Rĸ'I.ČĪĮÎdĩ†;ŲúõxE§KjõŗdN pˆŅŅ/ŧ-Äž}˜ĀŲŗBÄÆNMÅÆļšØDxŽās;áÎVÄL¸?JpŪÉĪĮŅđÆXÎÁƒ<­ÂAzė1-Ē%vû’% ÅæÍ˜ĖT Ŗđx”J^/ƒĶā ö{Ŋ> œ<‰!ž:…sŽÉt7ČÍÅ͉˅O‚- Ü­­Ŋäåq…†/gÔyf6]QQ>_T”GŽ`!BÄÅų|qqBzûmŧ‚ûšÔTˇ;5ÕG8 îNr?°Xūę÷ŌČČõ /k]ĨÂgpE™Lx×dēŦ\y–„okŗŗ÷04Ą@¯WˆÄÄĒĒÄD!Ūz Ųŋʐ<X+$ž‚ĩ—™Ųښ™<Ŋr<|ąķôõáķž˙CllÄŨ•Fƒ÷Î;qū1ņIååx×ī_JKg }ÖXqqyyqą\ EJJAAJĘĸEũũ‹ ņŅGX”ŨŽwGGņÜéB­všÔęāeŽđúŠõŊw/ūâčQ ą¸8df&œsÖ­Ã'íŲÃ-ĐÜî L ™„‡ááBÂͲLVX(“-[fĩ.[&ÄøøLŋ‡pxmnBĨr:U*^%8 ŪtŦ$W‚áaüÅČÎ<……ÛaÍŪ|ã“ēēpœčtûÉ&đqšvíršd2ĢU&3ÖŦ1ŒÆĩkÆˆˆ;#"„xņEgĻ5„Fãph4ŧZsŧԒËĀķĪã/ēēpȍ2°z5eŋ¯īŪ­´|9žžžöÚ?‡ū/ › ëŋī&&ļ´āxxé%,Ū €Í&„Vkˇkĩ\ëœīj9ÅĻH _.qŠ,(Ā#ŧhŌ[Ų’’ĀÄÄ\ĸ !vœ4Ž´T§bįN,œúŦذ[ĀfÃV@œ¯Áŧ1ž¨T8XyžnŒÆ[AEŧā‹/æOČ 0žŖÉĖvCâ×ÉÆF!rrššrrx3ĮiđŦ!J‚$.įø´4ee'Āôt¨‘\`Ážœ<|X.÷ûårždÍæúzŗ™ßæ8 îxü>ĩ‚ÜCxOĘ#íbb˜‡īBĶ„7müŠÃŨŒĶā5ø!¯)üŸŋ’‹/}ŪžĖũFŪ$üVÍiđĖ-0LæĢDļ ŸáY‹ŋw,DY øq÷áī¤ WĘ˙ūëô› ųúĸaIENDŽB`‚libspng-0.7.4/tests/images/tbgn3p08.png000066400000000000000000000027331442617636000176600ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_âPLTE˙˙˙€VVĩĩ¸¨BBŸŸŸą ‹Y›››„™™™§———•••“““))V‘‘‘›‹•‹..§€‹‹‹š‰‰‰ Õxuu‡‡‡˛………Ĩہž}}}É{{{yyy77VwwwuuusssHŠHŽdbV‰V((|S‹S‰‰gggeee]m]å†&&o--D‘Daaa;;D‰D=“=¤ķņYYYWWWUUUSSS4…4QQQ$—$OOO::Aē˛ĮÅüüü×Á-w-úúúŋhŊÚÔÔ{ ­ øøøšˇœœĄööö Ą ŗą‘ĢōōōЧîîîėėė—ęęęk‹‰‡111*@Ž îĶÖĶĖĖĖ“Ŗ**ÆÆÆÄÄÄĖĶ kkx>>mŸ VFFHAAMs]]Q¨¨¨ííī   žžžœœœšššš˛˜˜˜ë–––ž”””’’’ŽŽŽ‘ŠŠŠˆˆˆvĸv…ˆ…†††„„„x‚‚‚~‚~~~~|||zzzJĀJvvvtttrrrppp˜nnnjpjzffjjj„DĸDK–Kadabbbô8˜8\\\ZZZæ]BxBVVVđ.”.GhG11`ØRRRPPPÎ!˜!mČLLLũũũƝokkęHHHŧ4f4õSS`°Žˇ¤īīīĸOO•44˜–’įįįŒãã〒odVYdNNkĪĪĪŨŨā{ÉÉÉA!!YWWYDDxŋŋŋëŨŨ--T `˙ŋ}}ĒĒĒxe=5tRNS@æØfbKGDõEŌÛŠIDAT8c` 2xđ* ė=ĻÅfO,™ŋ|Ā*›ų^Ę´­Moƒø†ÕU_"PeĶjøVži°Ŧœ\)žĀĒŊŊ}ÃŖu]Īc`˛1¯ú–ë´Y>šŦĐ_[+ŠĀfš@|ĩŪ“5:Ë9k2Á *”—kkߒėŋę{ë&ĢĸâĉŠĩ’âg´`}XÁ ųŲķÕ}YoĘhiiÕ3ם=[WÅdŽÚ~č=XAÎ|Ĩš3¤”5PÁō-އO܃¸a‘į5[~áųę0°tŠÆz—Ã'Î9C]9+é”ã˛i|ŧSd—@ÁĘíFAQ- Ą ĻEGlĩœÂËÍÉ-ĄŧxąĒ֖K>j3ÃÃ:  Ļ$&íÜ:‡_dŖ.OwΌÅëwŧ•ŪÛ´ĢĀŖĒ ãhęņ­ËĻéžOßĮÎŌĩt‡ŗčgĩ]ÅS›5aÁɴN"ßŪÖĶͲÂųŲÅŊŪį^ Ŧ„eĩ{rŪĮMļ~ˇí9Ųģ× pxĶÕ43Ļ rwōÃX§ÛSŲö}œėĻoËö6RyųŠĻ g÷•Â㞁wė'ôõØŧûÜËņúen.GLAĀūô;!ÁūwƒīnâåîÛ!z‘ëcąūcĄgˆØ^{íÚūăųay›øx'š”î o-:&—k‚P vãōģãŲįī[ķķ īņéÕo9āpēšĄ ”Į}ŗYô•Ø|cA~)×Ō͟Éq0Ą$ŠįkĖö%Å *šFqésÍ<ö-IfÍ3ŋœT`,&ŧÔõcãSŖ č  ãčåBC ąnŪÅ%ī:1䁠|[ČĸŲkyi…b“g`¸Ū5ŪlSÃ]'ąËÁ{ųy6‡˜pJÁ‹éføäā—Į Ž˙‘•ÛĀIENDŽB`‚libspng-0.7.4/tests/images/tbrn2c08.png000066400000000000000000000031411442617636000176470ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA† 1č–_tRNS˙˙˙7X}bKGD˙3'|ķôIDATH‰Í– LĶwĮŋ˙Rl0E h• †‘ ˜‘D°)¨ÄP)Ĩ”A""āč`Č͉…Ą”W)”÷û!A §ÎsÃe"ƒ0Áe!fL4sNƒi|ąkɖ b6“]B(ŋûÜŨīî{?ĖžaÃ˙pįΝŪŪŪÉÉÉ˙pīŪŊļļļ´´´ˆˆˆX˙4GĮĶ=Z탑‘īęęĘÍ͍ŠŠR„†uuÍ33Ķ* ČĒĻÉÁĄKŠ|öėŲRSSSW¯^-++‹_ĄHŲĩ+{ũú2†iŠ€x.7ĖÁA˛b…ëyÅ˖ŒlllxüøąaĀŖGúúúĒĢĢ“““Ž…†f;:–p8õĀYāĐ —&†Éˇ°PŲÛ§››gå@! Hbccé ¯ŠŠĄŠķũũũéééôu’Ūʝœ>>ځs\néļmÅbņe›!ë60üü¤G~(†rMú‡EGGSŦķÃÃÃ*•*'''33355UĐan~ÖĘJãįw$))å/ËP(Zvė˛ļ[ĩęW.wĐĖė”]jPĐß˙âīīßŲŲ9p˙ū}r‘‘ĄĘQĐŨRBŧŽQöááábąØ××÷îŨģķt9é™éļļėSl×fWuŠē¤¤¤°°đĉŠ ÛQŊ‘wljL&›sííí-—Ë tQ^^s‰Á5` ōvyqq1õREEEiiivvöąėđáÃÁÁûíėTlv —{ÄÎ.ČĶĶįøņãä‘û1Ā4ŦGŦ‹ŠŠČ5ĒĒǍ1č§Z­ĻēĨéMŠTRÅ8 ‘ė73ûč§vŪĸėíĨ­­­äŽw’§kÛ:FrS2UISĄņŦ÷ô<ãYP_P___[[K7DĒIpp°LŧnŨ÷Āo@#‹ĩŸaD@˜••|hhČ ššyCå´ßéÂ>!åäĶ惝tŗāTíTWW×ĐĐ@Eu'īTč͛OŗĀ5#Ŗw,,Ūâr×pšRĄPōôéS€öövAĄMÔųĀprrËr=ÎxčfŦ .e.>%AŒÄÄDō.GĪPøFFq..žģwīæņ\)I€I&ëééqÎrÆE€įāwøÖø ë…č*!*ŅMPÄ Ą%€@pNūi[[š‡‡—40LAmíIÀ›7oē)Ũt€T€æxkÕk…ĩB]_U@T"ĸáœcüÉdr“q ŸÍVlŲĖáh’•cĻĻĄ¨a͚Wĸ—Ž>*˜Tš`8›J|ĢS÷b÷ĘĘJbPĄÂÂÂvîLׇ™ĪåņŧõA)čci8==m@’+~OŦĶļ28¤8ā L ŖûE‹=E{æZ–tÃA3đåä$ą°x›Å €šŊ{ãfįÛ<š>”pt°nnĢO­Æ€č āŖõ™›ģōōrZ>ŋ šģī\n p‰2ČĘ*^ ŸŸo\oLęėéŧ]ĩ}ēęëÚ4ŪŪ4Ä ũ \š˛ƒęcdéåååätēæĢ31 ęîî^ đäÉí­ŗĘ9ė`Xxtø˛ķËđ‰~ ¨!ŅHhļ‰A:(•J—/˙ˆ92ujQQÎŧāIļ tŗôˇk7Ģej-ˇ9ŽtH‚QjĢúKŽœ´ Ο˙ņ̀īßOš5ĢŦŦ¸8m•ĮcCNũ,Ŗm&lėdÍ[ ­gģVWĢmĶeÖz՞—W\\V6{ö‘#īŪÁ 8sĻžžŦŦüÄÍÍÍ˜ÍæÚĄڤȨ9ÎwžčžŨ5Đ1ËNÎü‚šYÎŧR(Čƚ5 nĀ;­­--ÕÕÉ.žîō‰¯ĘĸËÁ ˆ5ápØŪ ŋXoQßUI7!ĸ11AA{÷ xõĒĨĨąąŨŗ¯¯ŗŗžž/(+KN đņyö nĀM…îIW*l&ܘ<šŋŋššTAeeVVx8Hŗ§gd$J,tue^)™Õ×>mŸ VFFHAAMs]]Q¨¨¨ííī   žžžœœœšššš˛˜˜˜ë–––ž”””’’’ŽŽŽ‘ŠŠŠˆˆˆvĸv…ˆ…†††„„„x‚‚‚~‚~~~~|||zzzJĀJvvvtttrrrppp˜nnnjpjzffjjj„DĸDK–Kadabbbô8˜8\\\ZZZæ]BxBVVVđ.”.GhG11`ØRRRPPPÎ!˜!mČLLLũũũƝokkęHHHŧ4f4õSS`°Žˇ¤īīīĸOO•44˜–’įįįŒãã〒odVYdNNkĪĪĪŨŨā{ÉÉÉA!!YWWYDDxŋŋŋëŨŨ--T `˙ŋ}}%ˇ­tRNS@æØfbKGDˆHŠIDAT8c` 2xđ* ė=ĻÅfO,™ŋ|Ā*›ų^Ę´­Moƒø†ÕU_"PeĶjøVži°Ŧœ\)žĀĒŊŊ}ÃŖu]Īc`˛1¯ú–ë´Y>šŦĐ_[+ŠĀfš@|ĩŪ“5:Ë9k2Á *”—kkߒėŋę{ë&ĢĸâĉŠĩ’âg´`}XÁ ųŲķÕ}YoĘhiiÕ3ם=[WÅdŽÚ~č=XAÎ|Ĩš3¤”5PÁō-އO܃¸a‘į5[~áųę0°tŠÆz—Ã'Î9C]9+é”ã˛i|ŧSd—@ÁĘíFAQ- Ą ĻEGlĩœÂËÍÉ-ĄŧxąĒ֖K>j3ÃÃ:  Ļ$&íÜ:‡_dŖ.OwΌÅëwŧ•ŪÛ´ĢĀŖĒ ãhęņ­ËĻéžOßĮÎŌĩt‡ŗčgĩ]ÅS›5aÁɴN"ßŪÖĶͲÂųŲÅŊŪį^ Ŧ„eĩ{rŪĮMļ~ˇí9Ųģ× pxĶÕ43Ļ rwōÃX§ÛSŲö}œėĻoËö6RyųŠĻ g÷•Â㞁wė'ôõØŧûÜËņúen.GLAĀūô;!ÁūwƒīnâåîÛ!z‘ëcąūcĄgˆØ^{íÚūăųay›øx'š”î o-:&—k‚P vãōģãŲįī[ķķ īņéÕo9āpēšĄ ”Į}ŗYô•Ø|cA~)×Ō͟Éq0Ą$ŠįkĖö%Å *šFqésÍ<ö-IfÍ3ŋœT`,&ŧÔõcãSŖ č  ãčåBC ąnŪÅ%ī:1䁠|[ČĸŲkyi…b“g`¸Ū5ŪlSÃ]'ąËÁ{ųy6‡˜pJÁ‹éføäā—Į Ž˙‘•ÛĀIENDŽB`‚libspng-0.7.4/tests/images/tbyn3p08.png000066400000000000000000000027331442617636000177020ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_âPLTE˙˙˙€VVĩĩ¸¨BBŸŸŸą ‹Y›››„™™™§———•••“““))V‘‘‘›‹•‹..§€‹‹‹š‰‰‰ Õxuu‡‡‡˛………Ĩہž}}}É{{{yyy77VwwwuuusssHŠHŽdbV‰V((|S‹S‰‰gggeee]m]å†&&o--D‘Daaa;;D‰D=“=¤ķņYYYWWWUUUSSS4…4QQQ$—$OOO::Aē˛ĮÅüüü×Á-w-úúúŋhŊÚÔÔ{ ­ øøøšˇœœĄööö Ą ŗą‘ĢōōōЧîîîėėė—ęęęk‹‰‡111*@Ž îĶÖĶĖĖĖ“Ŗ**ÆÆÆÄÄÄĖĶ kkx>>mŸ VFFHAAMs]]Q¨¨¨ííī   žžžœœœšššš˛˜˜˜ë–––ž”””’’’ŽŽŽ‘ŠŠŠˆˆˆvĸv…ˆ…†††„„„x‚‚‚~‚~~~~|||zzzJĀJvvvtttrrrppp˜nnnjpjzffjjj„DĸDK–Kadabbbô8˜8\\\ZZZæ]BxBVVVđ.”.GhG11`ØRRRPPPÎ!˜!mČLLLũũũƝokkęHHHŧ4f4õSS`°Žˇ¤īīīĸOO•44˜–’įįįŒãã〒odVYdNNkĪĪĪŨŨā{ÉÉÉA!!YWWYDDxŋŋŋëŨŨ--T `˙ŋ}}˙˙ãŨ2‰tRNS@æØfbKGDõEŌÛŠIDAT8c` 2xđ* ė=ĻÅfO,™ŋ|Ā*›ų^Ę´­Moƒø†ÕU_"PeĶjøVži°Ŧœ\)žĀĒŊŊ}ÃŖu]Īc`˛1¯ú–ë´Y>šŦĐ_[+ŠĀfš@|ĩŪ“5:Ë9k2Á *”—kkߒėŋę{ë&ĢĸâĉŠĩ’âg´`}XÁ ųŲķÕ}YoĘhiiÕ3ם=[WÅdŽÚ~č=XAÎ|Ĩš3¤”5PÁō-އO܃¸a‘į5[~áųę0°tŠÆz—Ã'Î9C]9+é”ã˛i|ŧSd—@ÁĘíFAQ- Ą ĻEGlĩœÂËÍÉ-ĄŧxąĒ֖K>j3ÃÃ:  Ļ$&íÜ:‡_dŖ.OwΌÅëwŧ•ŪÛ´ĢĀŖĒ ãhęņ­ËĻéžOßĮÎŌĩt‡ŗčgĩ]ÅS›5aÁɴN"ßŪÖĶͲÂųŲÅŊŪį^ Ŧ„eĩ{rŪĮMļ~ˇí9Ųģ× pxĶÕ43Ļ rwōÃX§ÛSŲö}œėĻoËö6RyųŠĻ g÷•Â㞁wė'ôõØŧûÜËņúen.GLAĀūô;!ÁūwƒīnâåîÛ!z‘ëcąūcĄgˆØ^{íÚūăųay›øx'š”î o-:&—k‚P vãōģãŲįī[ķķ īņéÕo9āpēšĄ ”Į}ŗYô•Ø|cA~)×Ō͟Éq0Ą$ŠįkĖö%Å *šFqésÍ<ö-IfÍ3ŋœT`,&ŧÔõcãSŖ č  ãčåBC ąnŪÅ%ī:1䁠|[ČĸŲkyi…b“g`¸Ū5ŪlSÃ]'ąËÁ{ųy6‡˜pJÁ‹éføäā—Į Ž˙‘•ÛĀIENDŽB`‚libspng-0.7.4/tests/images/tm3n3p02.png000066400000000000000000000001641442617636000175750ustar00rootroot00000000000000‰PNG  IHDR ’g PLTE˙˙˙˙ĩŸCÎtRNSUĒ š'9IDATx^cd‚P ,ŒUƒ‹+a3.AIENDŽB`‚libspng-0.7.4/tests/images/tp0n0g08.png000066400000000000000000000013171442617636000175700ustar00rootroot00000000000000‰PNG  IHDR V%(gAMA† 1č–_†IDAT8c¨'HTđøäG< ^l­KrK\øĢ‚wû;Ķ}Í´4T4ô&^ųƒĒāĶ‘ŠE9Ávz:ęÚļĘŌ9 Î˙€)ø~jVYqš§ĄąŠ…ĄĢąēŽvT^qŲė#īĀ Îԗ•–ú™›™Û„Ļ8šx¸:ŲY¨å”AÖ,°‚;­-ÕÉ.îIeåååE‰áA~Ū~É@fALĐ^°‚W-í}õ•¨ ,9$ĀįXÁĻŒ¤Š “û›Ģa ĒĒ2+<ĀĮ3ę‹ŽĖ’žūIS§Oi¯‚Âhc ]ˇ&¨‚I™ųũũ§LŸ9{fw}]]myRˆĒ¸ ū¨‚)é™-ũ&OŽĒŸ>oN_]VT¸Ž´/ˇĘ5¨‚%é)Ĩũũ“šķ’ķæÎ_Pi'+Æ+Å+b÷Ē`[Z|jW߄瞍ŧ9ķæ—D+Hķ[yHÁ‚úxJFpu_UiidÁėšķō"­d…ĩ]”„įÂÜHÎđČé.)‰ČŸ5{nz¸˛´€š¸ŸØq˜‚W‰Š~ąIE3fÍIô”U’áãáQūSđ'!9<ĸ°8ŧ0¤dúĖYQ–2üÆ2ŧŧBˆčÎKHÍŦ ,.ž:}Z¸Ž$ƒļ¸O;BAO|z`FYD^PáäŠũaJĸ|îĻBBBG ~-NNNĪLË ,8š3TFHĐRTDaJ’쐛š–‘PÚ?ą1T‘ŸG@@õZš|Ķ––žPÜÛ_ĸĪĮÍmø#ŅūY•žî_ÜŨ[l+-ėķ[˛ŋRQßŅäo^ū {žxŋ š­#=0`Á_lųūhmK ڎ,„žõîMH=SOAũ×Įõø`÷FĖQŲcÕ]IENDŽB`‚libspng-0.7.4/tests/images/tp0n2c08.png000066400000000000000000000030721442617636000175660ustar00rootroot00000000000000‰PNG  IHDR üíŖgAMA† 1č–_ņIDATH‰Í– L“WĮ˙ˇP” †‘ ĩ0#‰`¨ÄP)Ĩ”A""āč`Č͉…Ą”W)”÷û!A §ÎsÃe"ƒ0Áe!fL4sNƒi|m§%[F,ˆ™&;iž4_{ĪīÜsĪųŸ‹Œ7lønßžŨ××755õšwīŪmooOOOŒŒŒ Hwt<åéŲĢŅÜũO€‰‰‰îîîŧŧŧččhyXØW×|335 R, †aš‚n…bthčéͧKLOO_šrĨŧŧX6\ /_.d:^‰ąņ‘¨¨ĻĻĻÁÁÁGé<|ø°ŋŋŋĻĻ&%%%11ņhXXŽŖc)—ÛœÎ}Ā p hf˜KKĨƒC†šy&Prą8..ŽŌōÚÚZ ‘0000@Oú9YgNNŸŸĀY–-ÛēĩD$ēdk;l`p ˜~~Ō!ŋ C{Mū—ÅÄÄPŦķ###JĨ2777+++--MØin~ÆÚZíī899õo˔Ë[ˇoļą_šōW–23;ioŸüĪâããCCCēēēæîŨģGŽ333•šĘÂÂB:[zųÁĢí>"""00P$ųųųŨšsg@{8Yv•vœ“×WU™Ē´´´¨¨čøņãi ۝‘wʉT*síãã#“ÉôTQ~~>s‘ÁU`˛YII ÕReeeYYYNNÎŅėĐĄC!!ûėí•N)Ëļˇöōō=vė˜yd?fŅĖĀfÔϏ¸˜\ ēēš ƒž*•Šū–Ž3…BAßŋŋXŧĪĖėk`€Ęxˆvp´ĩĩé;‹ÚŌšĨe¤4§P–Ô•j¯/¯Ķ^… … uuutB œ„„„HĨ!k×~ü4ėc7 ÜÚZ6<<ŦĐŌŌ˛žj=Zī´aŋöäÛæ‹¯´ŊāTãT__ߨØH Ey'ī”čM›NW ßą´|‹eWŗŦD(?yōD ŖŖƒ_ÄG3U>pÜÜŧō<ĪĶžÚ̆Kš …O› FRRy‰ĸŒf)|CÃx¯]ģvYX¸ōų’=LÖÛÛëœíŒ )ÎĀīđĢõ6Ņ TÁ­ĖN‚6A jZđųgu៲ŗ“yzú[X¤ SXWwB?āÆî w-  >žÂÕaP[W•p+uŖæœcüIĨ2“ ŸÃ‘oŪÂåj’•ŖĻĻa¨~õšw’ˇ6?J˜T™`8 Û*[|ĢUĒĒ*bPĸÂÃÃwėČЅ‰Į ŗ°đŅ%§•UĐĖˌ~IŽč=‘VÛĘ!Hā L)ŖũĸÁîâŨs%K :a>ŋøČvr[Zžm`$ĩ{öÄgˎyr}0ņ ha Ü#ŨW\…Đ ÂWã;×wÔ´<^;0ôđØÍįķY6¸H;ČÎ.Y PPP`Ô`DęėåŧMš ũÚėkË´>…>ÔÄ ũ Ząĸ“ōchåíííätÚâĢ71 îééY đøņcÍyŗŌ9ü@xDL„ņ9c|ĸ*ˆÕbęmbJ$’eË>"\n„ŗs(‡S \āņö´ˇ™ņ‚é™CCCąąą4E|Ē› y¨$;1Hq °z5eÃD2 MĪb+̏k׆^tĨ@6>>žŒsēÜæBœ'VĢÕÄ 6&MŪ¸ŅĮ€Ɔ’ąaCŌ͛?ëõŗ CWT­­­Zņ ™ iŽ”ÄŽÔÆbąØÍ͍Įŗ55õôöNxđ`r!'‹æėúđu—ZĒZEĻ‚æ1({4°üũũˇlŲB‚JĮ—ß‹&''IāhōPԈAmŧwī^š-ôōŲŗg/]ž¤›ŨķįĪ/_žLC›ÔÆ´ƒÎÎÎĨ,\*`ÎÆÆÆ4 U×ÜäõČfggé†úJKŪøíú/UęTĖfĮIENDŽB`‚libspng-0.7.4/tests/images/tp0n3p08.png000066400000000000000000000027041442617636000176050ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆgAMA† 1č–_ßPLTEm€VVĩĩ¸¨BBŸŸŸą ‹Y›››„™™™§———•••“““))V‘‘‘›‹•‹..§€‹‹‹š‰‰‰ Õxuu‡‡‡˛………Ĩہž}}}É{{{yyy77VwwwuuusssHŠHŽdbV‰V((|S‹S‰‰gggeee]m]å†&&o--D‘Daaa;;D‰D=“=¤ķņYYYWWWUUUSSS4…4QQQ$—$OOO::Aē˛ĮÅüüü×Á-w-úúúŋhŊÚÔÔ{ ­ øøøšˇœœĄööö Ą ŗą‘ĢōōōЧîîîėėė—ęęęk‹‰‡111*@Ž îĶÖĶĖĖĖ“Ŗ**ÆÆÆÄÄÄĖĶ kkx>>mŸ VFFHAAMs]]Q¨¨¨ííī   žžžœœœšššš˛˜˜˜ë–––ž”””’’’ŽŽŽ‘ŠŠŠˆˆˆvĸv…ˆ…†††„„„x‚‚‚~‚~~~~|||zzzJĀJvvvtttrrrppp˜nnnjpjzffjjj„DĸDK–Kadabbbô8˜8\\\ZZZæ]BxBVVVđ.”.GhG11`ØRRRPPPÎ!˜!˙˙˙ČLLLũũũƝokkęHHHŧ4f4õSS`°Žˇ¤īīīĸOO•44˜–’įįįŒãã〒odVYdNNkĪĪĪŨŨā{ÉÉÉA!!YWWYDDxŋŋŋëŨŨ--T `˙ŋ}}DbǐIDAT8cP&HTđāU÷˜›=ądūōĢ‚Ė÷RĻm lzÄ7ŦŽú€Ē ­†oå™ËĘɕâ ŦÚÛÛ7>mŸ VFFHAAMs]]Q¨¨¨ííī   žžžœœœšššš˛˜˜˜ë–––ž”””’’’ŽŽŽ‘ŠŠŠˆˆˆvĸv…ˆ…†††„„„x‚‚‚~‚~~~~|||zzzJĀJvvvtttrrrppp˜nnnjpjzffjjj„DĸDK–Kadabbbô8˜8\\\ZZZæ]BxBVVVđ.”.GhG11`ØRRRPPPÎ!˜!mČLLLũũũƝokkęHHHŧ4f4õSS`°Žˇ¤īīīĸOO•44˜–’įįįŒãã〒odVYdNNkĪĪĪŨŨā{ÉÉÉA!!YWWYDDxŋŋŋëŨŨ--T `˙ŋ}}%ˇ­tRNS@æØfŠIDAT8c` 2xđ* ė=ĻÅfO,™ŋ|Ā*›ų^Ę´­Moƒø†ÕU_"PeĶjøVži°Ŧœ\)žĀĒŊŊ}ÃŖu]Īc`˛1¯ú–ë´Y>šŦĐ_[+ŠĀfš@|ĩŪ“5:Ë9k2Á *”—kkߒėŋę{ë&ĢĸâĉŠĩ’âg´`}XÁ ųŲķÕ}YoĘhiiÕ3ם=[WÅdŽÚ~č=XAÎ|Ĩš3¤”5PÁō-އO܃¸a‘į5[~áųę0°tŠÆz—Ã'Î9C]9+é”ã˛i|ŧSd—@ÁĘíFAQ- Ą ĻEGlĩœÂËÍÉ-ĄŧxąĒ֖K>j3ÃÃ:  Ļ$&íÜ:‡_dŖ.OwΌÅëwŧ•ŪÛ´ĢĀŖĒ ãhęņ­ËĻéžOßĮÎŌĩt‡ŗčgĩ]ÅS›5aÁɴN"ßŪÖĶͲÂųŲÅŊŪį^ Ŧ„eĩ{rŪĮMļ~ˇí9Ųģ× pxĶÕ43Ļ rwōÃX§ÛSŲö}œėĻoËö6RyųŠĻ g÷•Â㞁wė'ôõØŧûÜËņúen.GLAĀūô;!ÁūwƒīnâåîÛ!z‘ëcąūcĄgˆØ^{íÚūăųay›øx'š”î o-:&—k‚P vãōģãŲįī[ķķ īņéÕo9āpēšĄ ”Į}ŗYô•Ø|cA~)×Ō͟Éq0Ą$ŠįkĖö%Å *šFqésÍ<ö-IfÍ3ŋœT`,&ŧÔõcãSŖ č  ãčåBC ąnŪÅ%ī:1䁠|[ČĸŲkyi…b“g`¸Ū5ŪlSÃ]'ąËÁ{ųy6‡˜pJÁ‹éføäā—Į Ž˙‘•ÛĀIENDŽB`‚libspng-0.7.4/tests/images/xc1n0g08.png000066400000000000000000000002121442617636000175510ustar00rootroot00000000000000‰PNG  IHDR î­BMgAMA† 1č–_AIDATxœcd`$Čŗ Œ)ø÷?`y00ʐgdĸy\ ŒđĘū˙Īø‡æq10Ęā•edPåūq5âØYIENDŽB`‚libspng-0.7.4/tests/images/xc9n2c08.png000066400000000000000000000002211442617636000175570ustar00rootroot00000000000000‰PNG  IHDR  +jĸgAMA† 1č–_HIDATxœíÕÁ 0 @…ė‘ũˇr;D+ôĄøÎá+´ ;€ ø íä}Lx@J‡„(đ t8#Š@ģpwŖĀ‚ąų^@ĪKIENDŽB`‚libspng-0.7.4/tests/images/xcrn0g04.png000066400000000000000000000002211442617636000176460ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_HIDATxœc``TR26vq MK+/g C•ÛŅAŽ*wæLrPšĢV‘#€ĘŨŊ›Tî™3ä rīŪ%G•ûîîę˙AúIENDŽB`‚libspng-0.7.4/tests/images/xcsn0g01.png000066400000000000000000000002441442617636000176510ustar00rootroot00000000000000‰PNG  IHDR [GYgAMA† 1č–_[IDATxœ-Ėą 0 ŅëŌ˛J z4o<‚Á aEQņāŠ/ĒęŌ¤„lΊ%SįS4Wâ˛!ŋK&=Bs%%^‹Ú˛žojĘ0i.)anéo0eđŋ‡I/CSUMIENDŽB`‚libspng-0.7.4/tests/images/xd0n2c08.png000066400000000000000000000002211442617636000175470ustar00rootroot00000000000000‰PNG  IHDR ĖhĻbgAMA† 1č–_HIDATxœíÕÁ 0 @…ė‘ũˇr;D+ôĄøÎá+´ ;€ ø íä}Lx@J‡„(đ t8#Š@ģpwŖĀ‚ąų^@ĪKIENDŽB`‚libspng-0.7.4/tests/images/xd3n2c08.png000066400000000000000000000002211442617636000175520ustar00rootroot00000000000000‰PNG  IHDR ‹ČܲgAMA† 1č–_HIDATxœíÕÁ 0 @…ė‘ũˇr;D+ôĄøÎá+´ ;€ ø íä}Lx@J‡„(đ t8#Š@ģpwŖĀ‚ąų^@ĪKIENDŽB`‚libspng-0.7.4/tests/images/xd9n2c08.png000066400000000000000000000002211442617636000175600ustar00rootroot00000000000000‰PNG  IHDR cúĢ˙gAMA† 1č–_HIDATxœíÕÁ 0 @…ė‘ũˇr;D+ôĄøÎá+´ ;€ ø íä}Lx@J‡„(đ t8#Š@ģpwŖĀ‚ąų^@ĪKIENDŽB`‚libspng-0.7.4/tests/images/xdtn0g01.png000066400000000000000000000000751442617636000176550ustar00rootroot00000000000000‰PNG  IHDR [GYgAMA† 1č–_IENDŽB`‚libspng-0.7.4/tests/images/xhdn0g08.png000066400000000000000000000002121442617636000176410ustar00rootroot00000000000000‰PNG  IHDR CSUMgAMA† 1č–_AIDATxœcd`$Čŗ Œ)ø÷?`y00ʐgdĸy\ ŒđĘū˙Īø‡æq10Ęā•edPåūq5âØYIENDŽB`‚libspng-0.7.4/tests/images/xlfn0g04.png000066400000000000000000000002211442617636000176430ustar00rootroot00000000000000‰PNG  IHDR “áČ)gAMA† 1č–_HIDATxœc``TR26vq MK+/g C•ÛŅAŽ*wæLrPšĢV‘#€ĘŨŊ›Tî™3ä rīŪ%G•ûîîę˙AúIENDŽB`‚libspng-0.7.4/tests/images/xs1n0g01.png000066400000000000000000000002441442617636000175670ustar00rootroot00000000000000 PNG  IHDR [GYgAMA† 1č–_[IDATxœ-Ėą 0 ŅëŌ˛J z4o<‚Á aEQņāŠ/ĒęŌ¤„lΊ%SįS4Wâ˛!ŋK&=Bs%%^‹Ú˛žojĘ0i.)anéo0eđŋ‡I/Đ/ÉIENDŽB`‚libspng-0.7.4/tests/images/xs2n0g01.png000066400000000000000000000002441442617636000175700ustar00rootroot00000000000000‰QNG  IHDR [GYgAMA† 1č–_[IDATxœ-Ėą 0 ŅëŌ˛J z4o<‚Á aEQņāŠ/ĒęŌ¤„lΊ%SįS4Wâ˛!ŋK&=Bs%%^‹Ú˛žojĘ0i.)anéo0eđŋ‡I/Đ/ÉIENDŽB`‚libspng-0.7.4/tests/images/xs4n0g01.png000066400000000000000000000002441442617636000175720ustar00rootroot00000000000000‰PNg  IHDR [GYgAMA† 1č–_[IDATxœ-Ėą 0 ŅëŌ˛J z4o<‚Á aEQņāŠ/ĒęŌ¤„lΊ%SįS4Wâ˛!ŋK&=Bs%%^‹Ú˛žojĘ0i.)anéo0eđŋ‡I/Đ/ÉIENDŽB`‚libspng-0.7.4/tests/images/xs7n0g01.png000066400000000000000000000002441442617636000175750ustar00rootroot00000000000000‰PNG IHDR [GYgAMA† 1č–_[IDATxœ-Ėą 0 ŅëŌ˛J z4o<‚Á aEQņāŠ/ĒęŌ¤„lΊ%SįS4Wâ˛!ŋK&=Bs%%^‹Ú˛žojĘ0i.)anéo0eđŋ‡I/Đ/ÉIENDŽB`‚libspng-0.7.4/tests/images/z00n2c08.png000066400000000000000000000061441442617636000174770ustar00rootroot00000000000000‰PNG  IHDR üíŖ +IDATxÚ ßķ˙˙øø÷øøø÷øøøø÷øøø÷øøø÷øøøø÷øøø÷øøøø÷ ø ø ø ÷  ø  ø  ø  ø  ÷   ø   ø   ø   ÷    ø    ø    ø    ÷     ø     ø     ø     ø     ÷      ø      ø      ø      ÷       ø       ø       U“Nnđ•IENDŽB`‚libspng-0.7.4/tests/images/z03n2c08.png000066400000000000000000000003501442617636000174730ustar00rootroot00000000000000‰PNG  IHDR üíŖ¯IDATx^ĩŅŨ@0 āJ\đūËŨl" [ןĶED‚ī¤=SJ´Sšļķūf^Õŋô>› x–Đ€…(ä9CN҉֠ .ũœßÁŖGŧtx_ÛACލ­Ŗē:$€Ķũ%tgÉcŨŗ"‘nęļēĄŽ]‘ZWXtyF]؁]—ŦČĨŧ:Й’1z¯d˜Ū\R˙€õOxŊî DŋWĨį€U“NnßÛCIENDŽB`‚libspng-0.7.4/tests/images/z06n2c08.png000066400000000000000000000003401442617636000174750ustar00rootroot00000000000000‰PNG  IHDR üíŖ§IDATxœĩŅK€ К°Āûv¸1~a>W$’×Đn­Ą(8ŨyōKr-Ą"ōËĄČ %Pö œzPEˇđŌéô:wƒNŦhŦŗ~uJĀL÷ŧН#¯uOE"Ũ Õm(tÃ:][‘ZWXtųF]¸]—TäŌ—^}@Đ'#sôŋ‘iú°"Ļū ëŨ|ũšAˆ~UĨųU“Nn1RÁ,IENDŽB`‚libspng-0.7.4/tests/images/z09n2c08.png000066400000000000000000000003401442617636000175000ustar00rootroot00000000000000‰PNG  IHDR üíŖ§IDATxÚĩŅK€ К°Āûv¸1~a>W$’×Đn­Ą(8ŨyōKr-Ą"ōËĄČ %Pö œzPEˇđŌéô:wƒNŦhŦŗ~uJĀL÷ŧН#¯uOE"Ũ Õm(tÃ:][‘ZWXtųF]¸]—TäŌ—^}@Đ'#sôŋ‘iú°"Ļū ëŨ|ũšAˆ~UĨųU“NnÖžõIENDŽB`‚libspng-0.7.4/tests/meson.build000066400000000000000000000022401442617636000164730ustar00rootroot00000000000000if get_option('dev_build') == false subdir_done() endif add_languages('cpp', native : false) cpp = meson.get_compiler('cpp') fuzzer_args = [] if cc.has_function('fmemopen', prefix : '#include ', args : '-D_GNU_SOURCE') fuzzer_args = [ '-D_GNU_SOURCE', '-DSPNGT_HAVE_FMEMOPEN' ] endif read_fuzzer = executable('fuzz_repro', 'fuzz_main.c', 'spng_read_fuzzer.c', c_args : fuzzer_args, link_with : spng_lib ) write_fuzzer_exe = executable('fuzz_write_repro', 'fuzz_main.c', 'spng_write_fuzzer.c', c_args : fuzzer_args, link_with : spng_lib ) png_dep = dependency('libpng', version : '>=1.6.0', fallback : ['libpng', 'png_dep']) test_deps = [ spng_dep, png_dep ] test_exe = executable('testsuite', 'testsuite.c', dependencies : test_deps) test('info', test_exe, args : 'info') cpp_exe = executable('cpp_exe', 'test.cpp', dependencies : spng_dep) test('cpp_test', cpp_exe) subdir('images') subdir('crashers') subdir('misc') if get_option('oss_fuzz') == false subdir_done() endif corpora = subproject('fuzzing_corpora').get_variable('corpora') foreach case : corpora test('testcase', read_fuzzer, args : case) endforeachlibspng-0.7.4/tests/misc/000077500000000000000000000000001442617636000152665ustar00rootroot00000000000000libspng-0.7.4/tests/misc/icc_profile.png000066400000000000000000000012251442617636000202520ustar00rootroot00000000000000‰PNG  IHDR Ŧˆ1ā…iCCPICC profile(‘}‘=HÃ@†ßĻJE*‚vqČP,ŠŠ8–*ÁBi+´ę`ré4iHR\ׂƒ?‹Ug]\AđÄÍÍIŅEJü.)´ˆņŽãŪûŪ—ģīĄQaĒŲ5 ¨še¤â11›[¯ĐĀ|3õDz1Īņußī"<ËģîĪҧäMøDâ(Ķ ‹xƒxvĶŌ9ī‡XIRˆĪ‰Į ē ņ#×e—ß8xfČȤæ‰CÄbąƒåf%C%ž!+ĒFųBÖe…ķgĩRc­{ōķÚJšë´FĮHB„ŒʨĀB„v):yø‡’\2šĘ`äX@*$Įūŋ{kϧܤ` č~ąíQ ° 4ëļũ}lÛÍĀ˙ \imĩĖ}’^oká# ¸¸nkōpš =é’!9’Ÿ–P(īgôM9`đč]sûÖ:Į鐥^-߇ĀX‘˛×=ŪŨĶŲˇkZũûTĪr›Æ]@ËIDATXÃÕÕ;Â0ĐAĸHŽΝ\+tĻ@Ą`đg×;“Š\Xoäų’RJĀ}>×ũ´Ķ¸ŋ“Ųŋ?šß´Č˜Ųũ`ß4é0ĢÜĀ7ēČåéôū҉;PF§ė@ l„ęé4ZéčŖ‡vĀ‚4BvôáŦéīŦšt˜oÎ7āIw!ē[€Qt‡w`,Ũôˆ PŊ;@4ŊŖôĻ0Ņ+Gˆ^€•^ĐnúĪ(Đ3#¤C?PŖ?׿Bū^ ĘzĪIENDŽB`‚libspng-0.7.4/tests/misc/meson.build000066400000000000000000000001731442617636000174310ustar00rootroot00000000000000test('unknown_chunk', test_exe, args : files('unknown.png')) test('icc_profile', test_exe, args : files('icc_profile.png'))libspng-0.7.4/tests/misc/unknown.png000066400000000000000000000002441442617636000174730ustar00rootroot00000000000000‰PNG  IHDR@@ĒiqŪeTXtabcdefghijklmnopqrstbNËčKIDATxœíĐA Ā0ĀŋĻ͆ ’=h,Û33ëc§¨PÔ ¨jÔ5ꀚu@̀: f@đÚŲ/›Ę!IENDŽB`‚libspng-0.7.4/tests/ossfuzz.sh000077500000000000000000000031611442617636000164160ustar00rootroot00000000000000#!/bin/bash -eu # This script is meant to be run by # https://github.com/google/oss-fuzz/blob/master/projects/libspng/Dockerfile mkdir $SRC/zlib/build $SRC/libspng/build cd $SRC/zlib/build cmake .. make -j$(nproc) $CC $CFLAGS -c -I$SRC/zlib/build -I$SRC/zlib \ $SRC/libspng/spng/spng.c \ -o $SRC/libspng/build/libspng.o \ cp $SRC/zlib/build/libz.a $SRC/libspng/build/libz.a cd $SRC/libspng/build ar x libz.a ar rcs libspng_static.a *.o $CXX $CXXFLAGS -std=c++11 \ $SRC/libspng/tests/spng_read_fuzzer.c \ -DSPNGT_HAVE_FMEMOPEN=1 \ -o $OUT/spng_read_fuzzer \ $LIB_FUZZING_ENGINE $SRC/libspng/build/libspng_static.a $SRC/zlib/build/libz.a $CXX $CXXFLAGS -std=c++11 -I$SRC/zlib/build -I$SRC/zlib \ $SRC/libspng/tests/spng_read_fuzzer.c \ -DSPNGT_HAVE_FMEMOPEN=1 \ -o $OUT/spng_read_fuzzer_structure_aware \ -include $SRC/fuzzer-test-suite/libpng-1.2.56/png_mutator.h \ -D PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR \ $LIB_FUZZING_ENGINE $SRC/libspng/build/libspng_static.a $SRC/zlib/build/libz.a $CXX $CXXFLAGS -std=c++11 \ $SRC/libspng/tests/spng_write_fuzzer.c \ -DSPNGT_HAVE_FMEMOPEN=1 \ -o $OUT/spng_write_fuzzer \ $LIB_FUZZING_ENGINE $SRC/libspng/build/libspng_static.a $SRC/zlib/build/libz.a find $SRC/libspng/tests -name "*.png" | \ xargs zip $OUT/seed_corpus.zip cp $SRC/libspng/tests/spng.dict $OUT/ ln -sf $OUT/seed_corpus.zip $OUT/spng_read_fuzzer_seed_corpus.zip ln -sf $OUT/seed_corpus.zip $OUT/spng_read_fuzzer_structure_aware_seed_corpus.zip ln -sf $OUT/spng.dict $OUT/spng_read_fuzzer.dict ln -sf $OUT/spng.dict $OUT/spng_read_fuzzer_structure_aware.dict libspng-0.7.4/tests/spng.dict000066400000000000000000000011431442617636000161460ustar00rootroot00000000000000header_png="\x89PNG\x0d\x0a\x1a\x0a" section_idat="IDAT" section_iend="IEND" section_ihdr="IHDR" section_plte="PLTE" section_trns="tRNS" section_chrm="cHRM" section_gama="gAMA" section_iccp="iCCP" section_sbit="sBIT" section_srgb="sRGB" section_text="tEXt" section_ztxt="zTXt" section_itxt="iTXt" section_bkgd="bKGD" section_hist="hIST" section_phys="pHYs" section_splt="sPLT" section_time="tIME" section_offs="oFFs" section_exif="eXIf" # not parsed by libspng #section_frac="fRAc" #section_pcal="pCAL" #section_scal="sCAL" #section_ster="sTER" #section_gifg="gIFg" #section_gift="gIFt" #section_gifx="gIFx" libspng-0.7.4/tests/spng_read_fuzzer.c000066400000000000000000000120131442617636000200430ustar00rootroot00000000000000#define SPNG_UNTESTED #include "../spng/spng.h" #include struct buf_state { const uint8_t *data; size_t bytes_left; }; static int buffer_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length) { struct buf_state *state = (struct buf_state*)user; (void)ctx; if(length > state->bytes_left) return SPNG_IO_EOF; memcpy(dest, state->data, length); state->bytes_left -= length; state->data += length; return 0; } #ifdef __cplusplus extern "C" #endif int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if(size < 4) return 0; int flags = data[size - 1]; int fmt = data[size - 3] | (data[size - 2] << 8); int stream = data[size - 4] & 1; int progressive = data[size - 4] & 2; int file_stream = data[size - 4] & 4; int discard = data[size - 4] & 8; size -= 4; int ret; unsigned char *out = NULL; FILE *file = NULL; spng_ctx *ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32); if(ctx == NULL) return 0; struct spng_ihdr ihdr; struct spng_plte plte; struct spng_trns trns; struct spng_chrm chrm; struct spng_chrm_int chrm_int; double gamma; struct spng_iccp iccp; struct spng_sbit sbit; uint8_t srgb_rendering_intent; struct spng_text text[4] = {0}; struct spng_bkgd bkgd; struct spng_hist hist; struct spng_phys phys; struct spng_splt splt[4] = {0}; struct spng_time time; struct spng_unknown_chunk chunks[4] = {0}; uint32_t n_text = 4, n_splt = 4, n_chunks = 4; struct buf_state state; state.data = data; state.bytes_left = size; if(stream) { if(file_stream) { #if defined(SPNGT_HAVE_FMEMOPEN) file = fmemopen((void*)data, size, "rb"); if(file == NULL) goto err; #endif ret = spng_set_png_file(ctx, file); } else ret = spng_set_png_stream(ctx, buffer_read_fn, &state); } else ret = spng_set_png_buffer(ctx, (void*)data, size); if(ret) goto err; spng_set_image_limits(ctx, 200000, 200000); spng_set_chunk_limits(ctx, 4 * 1000 * 1000, 8 * 1000 * 1000); spng_set_crc_action(ctx, SPNG_CRC_USE, discard ? SPNG_CRC_DISCARD : SPNG_CRC_USE); spng_set_option(ctx, SPNG_KEEP_UNKNOWN_CHUNKS, 1); size_t out_size; if(spng_decoded_image_size(ctx, fmt, &out_size)) goto err; if(out_size > 80000000) goto err; out = (unsigned char*)malloc(out_size); if(out == NULL) goto err; spng_get_ihdr(ctx, &ihdr); spng_get_plte(ctx, &plte); spng_get_trns(ctx, &trns); spng_get_chrm(ctx, &chrm); spng_get_chrm_int(ctx, &chrm_int); spng_get_gama(ctx, &gamma); spng_get_iccp(ctx, &iccp); spng_get_sbit(ctx, &sbit); spng_get_srgb(ctx, &srgb_rendering_intent); if(!spng_get_text(ctx, text, &n_text)) {/* Up to 4 entries were read, get the actual count */ spng_get_text(ctx, NULL, &n_text); uint32_t i; for(i=0; i < n_text; i++) {/* All strings should be non-NULL */ if(text[i].text == NULL || text[i].keyword[0] == '\0' || text[i].language_tag == NULL || text[i].translated_keyword == NULL || memchr(text[i].keyword, 0, 80) == NULL) { spng_ctx_free(ctx); free(out); return 1; } /* This shouldn't cause issues either */ text[i].length = strlen(text[i].text); } } spng_get_bkgd(ctx, &bkgd); spng_get_hist(ctx, &hist); spng_get_phys(ctx, &phys); if(!spng_get_splt(ctx, splt, &n_splt)) {/* Up to 4 entries were read, get the actual count */ spng_get_splt(ctx, NULL, &n_splt); uint32_t i; for(i=0; i < n_splt; i++) { if(splt[i].name[0] == '\0' || splt[i].entries == NULL || memchr(splt[i].name, 0, 80) == NULL) { spng_ctx_free(ctx); free(out); return 1; } } } if(!spng_get_unknown_chunks(ctx, chunks, &n_chunks)) { spng_get_unknown_chunks(ctx, NULL, &n_chunks); uint32_t i; for(i=0; i < n_chunks; i++) { if( (chunks[i].length && !chunks[i].data) || (!chunks[i].length && chunks[i].data) ) { spng_ctx_free(ctx); free(out); return 1; } } } if(progressive) { if(spng_decode_image(ctx, NULL, 0, fmt, flags | SPNG_DECODE_PROGRESSIVE)) goto err; size_t ioffset, out_width = out_size / ihdr.height; struct spng_row_info ri; do { if(spng_get_row_info(ctx, &ri)) break; ioffset = ri.row_num * out_width; }while(!spng_decode_row(ctx, out + ioffset, out_size)); } else if(spng_decode_image(ctx, out, out_size, fmt, flags)) goto err; spng_get_time(ctx, &time); err: spng_ctx_free(ctx); free(out); if(file != NULL) fclose(file); return 0; } libspng-0.7.4/tests/spng_write_fuzzer.c000066400000000000000000000103551442617636000202710ustar00rootroot00000000000000#include "../spng/spng.h" #include struct buf_state { const uint8_t *data; size_t bytes_left; }; static int stream_write_fn(spng_ctx *ctx, void *user, void *data, size_t length) { struct buf_state *state = (struct buf_state*)user; (void)ctx; if(length > state->bytes_left) return SPNG_IO_EOF; state->bytes_left -= length; if(state->data) { memcpy(data, state->data, length); state->data += length; } return 0; } #define copy_val(val) \ if(size < sizeof(val)) goto err; \ memcpy(&val, data, sizeof(val)); \ data += sizeof(val); \ size -= sizeof(val) #ifdef __cplusplus extern "C" #endif int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { uint8_t params[4]; if(size < sizeof(params)) return 0; memcpy(params, data, sizeof(params)); size -= sizeof(params); data += sizeof(params); //int flags = params[0]; int fmt = params[2] | (params[1] << 8); int stream = params[3] & 1; int progressive = params[3] & 2; //int file_stream = params[3] & 4; int get_buffer = params[3] & 8; int ret; size_t png_size = 0; void *png = NULL; struct buf_state state; unsigned char *img = NULL; size_t img_size; size_t pixel_bits = 0; spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER); if(ctx == NULL) return 0; spng_set_image_limits(ctx, 200000, 200000); spng_set_chunk_limits(ctx, 4 * 1000 * 1000, 8 * 1000 * 1000); state.data = NULL; state.bytes_left = SIZE_MAX; spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, 1); if(stream) ret = spng_set_png_stream(ctx, stream_write_fn, &state); else ret = spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1); if(ret) goto err; struct spng_ihdr ihdr; struct spng_plte plte; struct spng_trns trns; struct spng_chrm chrm; struct spng_chrm_int chrm_int; double gamma; struct spng_sbit sbit; uint8_t srgb_rendering_intent; struct spng_bkgd bkgd; struct spng_hist hist; struct spng_phys phys; struct spng_time time; copy_val(ihdr); copy_val(plte); copy_val(trns); copy_val(chrm); copy_val(chrm_int); copy_val(gamma); copy_val(sbit); copy_val(srgb_rendering_intent); copy_val(bkgd); copy_val(hist); copy_val(phys); copy_val(time); if(spng_set_ihdr(ctx, &ihdr)) goto err; spng_set_plte(ctx, &plte); spng_set_trns(ctx, &trns); spng_set_chrm(ctx, &chrm); spng_set_chrm_int(ctx, &chrm_int); spng_set_gama(ctx, gamma); spng_set_sbit(ctx, &sbit); spng_set_srgb(ctx, srgb_rendering_intent); spng_set_bkgd(ctx, &bkgd); spng_set_hist(ctx, &hist); spng_set_phys(ctx, &phys); spng_set_time(ctx, &time); switch(ihdr.color_type) { case SPNG_COLOR_TYPE_GRAYSCALE: case SPNG_COLOR_TYPE_INDEXED: pixel_bits = ihdr.bit_depth; break; case SPNG_COLOR_TYPE_TRUECOLOR: pixel_bits = ihdr.bit_depth * 3; break; case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: pixel_bits = ihdr.bit_depth * 2; break; case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: pixel_bits = ihdr.bit_depth * 4; break; default: goto err; } img_size = ihdr.width * pixel_bits + 7; img_size /= 8; img_size *= ihdr.height; if(img_size > size) goto err; if(img_size > 80000000) goto err; img = (unsigned char*)data; if(progressive) { if(spng_encode_image(ctx, NULL, 0, fmt, SPNG_ENCODE_PROGRESSIVE | SPNG_ENCODE_FINALIZE)) goto err; size_t ioffset, img_width = img_size / ihdr.height; struct spng_row_info ri; do { if(spng_get_row_info(ctx, &ri)) break; ioffset = ri.row_num * img_width; }while(!spng_encode_row(ctx, img + ioffset, img_size)); } else if(spng_encode_image(ctx, img, img_size, fmt, SPNG_ENCODE_FINALIZE)) goto err; if(get_buffer) { png = spng_get_png_buffer(ctx, &png_size, &ret); // These are potential vulnerabilities if(png && !png_size) return 1; if(!png && png_size) return 1; if(ret && (png || png_size)) return 1; } err: spng_ctx_free(ctx); free(png); return 0; } libspng-0.7.4/tests/spngt_common.h000066400000000000000000000037271442617636000172200ustar00rootroot00000000000000#ifndef SPNGT_COMMON_H #define SPNGT_COMMON_H #include #include #define SPNG_FMT_RGB16 8 /* XXX: Remove when support is added */ #define SPNGT_FMT_VIPS (1 << 20) /* the sequence of libpng calls in libvips */ typedef struct spngt_chunk_bitfield { unsigned ihdr: 1; unsigned plte: 1; unsigned trns: 1; unsigned chrm: 1; unsigned gama: 1; unsigned iccp: 1; unsigned sbit: 1; unsigned srgb: 1; unsigned text: 1; unsigned ztxt: 1; unsigned itxt: 1; unsigned bkgd: 1; unsigned hist: 1; unsigned phys: 1; unsigned splt: 1; unsigned time: 1; unsigned offs: 1; unsigned exif: 1; unsigned unknown: 1; }spngt_chunk_bitfield; typedef struct spngt_chunk_data { uint32_t n_text, n_splt, n_plte_entries, n_unknown_chunks; spngt_chunk_bitfield have; /* only meant to be set by spng */ struct spng_ihdr ihdr; struct spng_plte plte; struct spng_trns trns; struct spng_chrm chrm; struct spng_chrm_int chrm_int; double gamma; uint32_t gamma_int; struct spng_iccp iccp; struct spng_sbit sbit; uint8_t srgb_rendering_intent; struct spng_text *text; struct spng_bkgd bkgd; struct spng_hist hist; struct spng_phys phys; struct spng_splt *splt; struct spng_time time; struct spng_offs offs; struct spng_exif exif; struct spng_unknown_chunk *chunks; }spngt_chunk_data; enum spngt_flags { SPNGT_COMPARE_CHUNKS = 1, SPNGT_PRELOAD_FILE = 2, /* Preload PNG from file and/or decode from buffer */ SPNGT_ENCODE_ROUNDTRIP = 4, SPNGT_EXTENDED_TESTS = 8, SPNGT_SKIP = 8192 }; enum spngt_source_type { SPNGT_SRC_FILE = 0, SPNGT_SRC_BUFFER }; struct spngt_source { enum spngt_source_type type; FILE *file; void *buffer; size_t png_size; }; typedef struct spngt_test_case { struct spngt_source source; int fmt; int flags; int test_flags; }spngt_test_case; #endif /* SPNGT_COMMON_H */libspng-0.7.4/tests/target_clones.c000066400000000000000000000003511442617636000173270ustar00rootroot00000000000000/* This will only be available for GCC with glibc for the foreseeable future */ __attribute__((target_clones("default,avx2"))) int f(int x) { return x + 3; } int main(int argc, char **argv) { int y = f(39); return 0; }libspng-0.7.4/tests/test.cpp000066400000000000000000000005431442617636000160200ustar00rootroot00000000000000#include #include int main(int argc, char **argv) { (void)argc; (void)argv; unsigned char buf[30] = {0}; spng_ctx *ctx = spng_ctx_new(0); spng_set_png_buffer(ctx, buf, 30); struct spng_plte plte; int e = spng_get_plte(ctx, &plte); std::cout << spng_strerror(e); spng_ctx_free(ctx); return 0; } libspng-0.7.4/tests/test_png.h000066400000000000000000000140761442617636000163370ustar00rootroot00000000000000#ifndef TEST_PNG_H #define TEST_PNG_H #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || defined(__BIG_ENDIAN__) #define SPNGT_BIG_ENDIAN #else #define SPNGT_LITTLE_ENDIAN #endif #include #include "spngt_common.h" #include #include struct buf_state { unsigned char *data; size_t bytes_left; }; static struct buf_state state; void libpng_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { struct buf_state *state = png_get_io_ptr(png_ptr); #if defined(SPNGT_STREAM_READ_INFO) printf("libpng bytes read: %lu\n", length); #endif if(length > state->bytes_left) { png_error(png_ptr, "read_fn error"); } memcpy(data, state->data, length); state->bytes_left -= length; state->data += length; } png_structp init_libpng(spngt_test_case *test_case, png_infop *iptr) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(png_ptr == NULL) { printf("libpng init failed\n"); return NULL; } png_infop info_ptr = png_create_info_struct(png_ptr); if(info_ptr == NULL) { printf("png_create_info_struct failed\n"); return NULL; } if(test_case->source.type == SPNGT_SRC_FILE) png_init_io(png_ptr, test_case->source.file); else if(test_case->source.type == SPNGT_SRC_BUFFER) { state.data = test_case->source.buffer; state.bytes_left = test_case->source.png_size; png_set_read_fn(png_ptr, &state, libpng_read_fn); } if(setjmp(png_jmpbuf(png_ptr))) { printf("libpng error\n"); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0); png_read_info(png_ptr, info_ptr); *iptr = info_ptr; return png_ptr; } unsigned char *getimage_libpng(png_structp png_ptr, png_infop info_ptr, size_t *out_size, int fmt, int flags) { unsigned char *volatile image = NULL; png_bytep *volatile row_pointers = NULL; if(setjmp(png_jmpbuf(png_ptr))) { printf("libpng error\n"); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if(image != NULL) free(image); if(row_pointers != NULL) free(row_pointers); return NULL; } png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type; int filter_method; if(!png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method)) { printf("png_get_IHDR failed\n"); return NULL; } if(flags & SPNG_DECODE_GAMMA) { double gamma; if(png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, 2.2, gamma); } if(fmt == SPNG_FMT_RGBA16) { png_set_gray_to_rgb(png_ptr); png_set_filler(png_ptr, 0xFFFF, PNG_FILLER_AFTER); /* png_set_palette_to_rgb() + png_set_tRNS_to_alpha() */ png_set_expand_16(png_ptr); } else if(fmt == SPNG_FMT_RGBA8) { png_set_gray_to_rgb(png_ptr); png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); /* png_set_palette_to_rgb() + png_set_expand_gray_1_2_4_to_8() + png_set_tRNS_to_alpha() */ png_set_expand(png_ptr); png_set_strip_16(png_ptr); } else if(fmt == SPNG_FMT_RGB8) { png_set_gray_to_rgb(png_ptr); png_set_strip_alpha(png_ptr); png_set_strip_16(png_ptr); } else if(fmt == SPNG_FMT_G8) /* assumes only <=8-bit grayscale images */ {/* TODO: support all input formats */ png_set_expand_gray_1_2_4_to_8(png_ptr); } else if(fmt == SPNG_FMT_GA16) /* assumes only 16-bit grayscale images */ {/* TODO: support all input formats */ if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) && (flags & SPNG_DECODE_TRNS)) { png_set_tRNS_to_alpha(png_ptr); } else { if(bit_depth == 16) png_set_filler(png_ptr, 0xFFFF, PNG_FILLER_AFTER); } } else if(fmt == SPNG_FMT_GA8) /* assumes only <=8-bit grayscale images */ {/* TODO: support all input formats */ if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) && (flags & SPNG_DECODE_TRNS)) { png_set_tRNS_to_alpha(png_ptr); } else { png_set_expand_gray_1_2_4_to_8(png_ptr); png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); } } else if(fmt == SPNGT_FMT_VIPS) { if(color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); #if defined(SPNGT_LITTLE_ENDIAN) png_set_swap(png_ptr); #endif } #if defined(SPNGT_LITTLE_ENDIAN) /* we want host-endian values unless it's SPNG_FMT_RAW */ if(fmt != SPNG_FMT_RAW) png_set_swap(png_ptr); #endif png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); size_t image_size = height * rowbytes; memcpy(out_size, &image_size, sizeof(size_t)); /* Neither library does zero-padding for <8-bit images, but we want the images to be bit-identical for memcmp() */ image = calloc(1, image_size); if(image == NULL) { printf("libpng: malloc() failed\n"); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } row_pointers = malloc(height * sizeof(png_bytep)); if(row_pointers == NULL) { printf("libpng: malloc() failed\n"); free(image); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } uint32_t k; for(k=0; k < height; k++) { row_pointers[k] = image + k * rowbytes; } png_read_image(png_ptr, row_pointers); png_read_end(png_ptr, info_ptr); free(row_pointers); return image; } #endif /* TEST_PNG_H */ libspng-0.7.4/tests/test_spng.h000066400000000000000000000102571442617636000165170ustar00rootroot00000000000000#ifndef TEST_SPNG_H #define TEST_SPNG_H #include "spngt_common.h" #include #include int spng_get_trns_fmt(spng_ctx *ctx, int *fmt) { if(ctx == NULL || fmt == NULL) return 1; struct spng_trns trns; struct spng_ihdr ihdr; if(spng_get_ihdr(ctx, &ihdr)) return 1; if(!spng_get_trns(ctx, &trns)) { if(ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) { if(ihdr.bit_depth == 16) *fmt = SPNG_FMT_RGBA16; else *fmt = SPNG_FMT_RGBA8; } else if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) { *fmt = SPNG_FMT_RGBA8; } else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) { if(ihdr.bit_depth == 16) *fmt = SPNG_FMT_GA16; else *fmt = SPNG_FMT_GA8; } } else return 1; return 0; } spng_ctx *init_spng(spngt_test_case *test_case, struct spng_ihdr *ihdr) { int r = 0; spng_ctx *ctx = spng_ctx_new(0); if(ctx == NULL) { printf("spng_ctx_new() failed\n"); return NULL; } if(test_case->source.type == SPNGT_SRC_FILE) r = spng_set_png_file(ctx, test_case->source.file); else if(test_case->source.type == SPNGT_SRC_BUFFER) r = spng_set_png_buffer(ctx, test_case->source.buffer, test_case->source.png_size); if(r) { printf("spng_set_png_file/buffer() error: %s\n", spng_strerror(r)); goto err; } r = spng_set_option(ctx, SPNG_KEEP_UNKNOWN_CHUNKS, 1); if(r) { printf("spng_set_option() error: %s\n", spng_strerror(r)); goto err; } r = spng_set_image_limits(ctx, 16000, 16000); if(r) { printf("spng_set_image_limits() error: %s\n", spng_strerror(r)); goto err; } r = spng_set_chunk_limits(ctx, 66 * 1000 * 1000, 66 * 1000* 1000); if(r) { printf("spng_set_chunk_limits() error: %s\n", spng_strerror(r)); goto err; } struct spng_ihdr tmp; if(!ihdr) ihdr = &tmp; r = spng_get_ihdr(ctx, ihdr); if(r) { printf("spng_get_ihdr() error: %s\n", spng_strerror(r)); goto err; } return ctx; err: spng_ctx_free(ctx); return NULL; } unsigned char *getimage_spng(spng_ctx *ctx, size_t *out_size, int fmt, int flags) { int r; size_t siz, out_width; unsigned char *out = NULL; struct spng_ihdr ihdr; struct spng_row_info row_info; r = spng_get_ihdr(ctx, &ihdr); if(r) { printf("spng_get_ihdr() error: %s\n", spng_strerror(r)); goto err; } r = spng_decode_chunks(ctx); if(r) { printf("spng_decode_chunks() error: %s\n", spng_strerror(r)); goto err; } if(fmt == SPNGT_FMT_VIPS) { fmt = SPNG_FMT_PNG; if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) fmt = SPNG_FMT_RGB8; else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr.bit_depth < 8) fmt = SPNG_FMT_G8; spng_get_trns_fmt(ctx, &fmt); /*printf("VIPS format: %s\n", fmt_str(fmt));*/ } r = spng_decoded_image_size(ctx, fmt, &siz); if(r) { printf("spng_decoded_image_size() error: %s\n", spng_strerror(r)); goto err; } *out_size = siz; out_width = siz / ihdr.height; /* Neither library does zero-padding for <8-bit images, but we want the images to be bit-identical for memcmp() */ out = calloc(1, siz); if(out == NULL) goto err; r = spng_decode_image(ctx, NULL, 0, fmt, flags | SPNG_DECODE_PROGRESSIVE); if(r) { printf("progressive spng_decode_image() error: %s\n", spng_strerror(r)); goto err; } do { r = spng_get_row_info(ctx, &row_info); if(r) break; r = spng_decode_row(ctx, out + row_info.row_num * out_width, out_width); }while (!r); if(r != SPNG_EOI) { printf("progressive decode error: %s\n", spng_strerror(r)); goto err; } r = spng_get_time(ctx, NULL); if(r == SPNG_ECHUNKAVAIL || r == SPNG_EINVAL) r = 0; if(r) { printf("error at end of image: %s\n", spng_strerror(r)); goto err; } return out; err: if(out != NULL) free(out); return NULL; } #endif /* TEST_SPNG_H */ libspng-0.7.4/tests/testsuite.c000066400000000000000000001364101442617636000165350ustar00rootroot00000000000000#include "spngt_common.h" #include "test_spng.h" #include "test_png.h" #include static int n_test_cases, actual_count; static struct spngt_test_case test_cases[100]; static int extended_tests(FILE *file, int fmt); const char* fmt_str(int fmt) { switch(fmt) { case SPNG_FMT_RGBA8: return "RGBA8"; case SPNG_FMT_RGBA16: return "RGBA16"; case SPNG_FMT_RGB8: return "RGB8"; case SPNG_FMT_GA8: return "GA8"; case SPNG_FMT_GA16: return "GA16"; case SPNG_FMT_G8: return "G8"; case SPNG_FMT_PNG: return "PNG"; case SPNG_FMT_RAW: return "RAW"; case SPNGT_FMT_VIPS: return "VIPS"; default: return " "; } } static void print_test_args(struct spngt_test_case *test_case) { char *type; if(test_case->test_flags & SPNGT_ENCODE_ROUNDTRIP) type = "Encode"; else type = "Decode"; printf("%s and compare %s", type, fmt_str(test_case->fmt)); char pad_str[] = " "; pad_str[sizeof(pad_str) - strlen(fmt_str(test_case->fmt))] = '\0'; printf(",%sFLAGS: ", pad_str); if(!test_case->flags && !test_case->test_flags) printf("(NONE)"); if(test_case->flags & SPNG_DECODE_TRNS) printf("TRNS "); if(test_case->flags & SPNG_DECODE_GAMMA) printf("GAMMA "); if(test_case->test_flags & SPNGT_COMPARE_CHUNKS) printf("COMPARE_CHUNKS "); if(test_case->test_flags & SPNGT_EXTENDED_TESTS) printf("EXTENDED "); if(test_case->test_flags & SPNGT_SKIP) printf("[SKIPPED]"); printf("\n"); fflush(stdout); } static void add_test_case(int fmt, int flags, int test_flags) { int n = n_test_cases; test_cases[n].fmt = fmt; test_cases[n].flags = flags; test_cases[n_test_cases++].test_flags = test_flags; if( !(test_flags & SPNGT_SKIP) ) actual_count++; } /* Returns 1 on different images with, allows a small difference if gamma corrected */ static int compare_images(const struct spng_ihdr *ihdr, int fmt, int flags, const unsigned char *img_spng, const unsigned char *img_png, size_t img_size) { uint32_t w, h; uint32_t x, y; uint32_t max_diff=0, diff_div = 50; /* allow up to ~2% difference for each channel */ uint8_t have_alpha = 1; uint8_t alpha_mismatch = 0; uint8_t pixel_diff = 0; uint8_t bytes_per_pixel = 4; /* SPNG_FMT_RGBA8 */ const uint8_t samples_per_byte = 8 / ihdr->bit_depth; const uint8_t mask = (uint16_t)(1 << ihdr->bit_depth) - 1; const uint8_t initial_shift = 8 - ihdr->bit_depth; uint8_t shift_amount = initial_shift; size_t row_width = img_size / ihdr->height; size_t px_ofs = 0; unsigned channels; uint32_t red_diff = 0, green_diff = 0, blue_diff = 0, sample_diff = 0; uint16_t spng_red = 0, spng_green = 0, spng_blue = 0, spng_alpha = 0, spng_sample = 0; uint16_t png_red = 0, png_green = 0, png_blue = 0, png_alpha = 0, png_sample = 0; w = ihdr->width; h = ihdr->height; if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) { if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR) { if(ihdr->bit_depth == 8) fmt = SPNG_FMT_RGB8; else fmt = SPNG_FMT_RGB16; } else if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) { if(ihdr->bit_depth == 8) fmt = SPNG_FMT_RGBA8; else fmt = SPNG_FMT_RGBA16; } else {/* gray 1,2,4,8,16 bits, indexed 1,2,4,8 or gray alpha 8,16 */ channels = 1; /* grayscale or indexed color */ if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) channels = 2; else have_alpha = 0; if(ihdr->bit_depth < 8) bytes_per_pixel = 1; else bytes_per_pixel = channels * (ihdr->bit_depth / 8); if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) flags &= ~SPNG_DECODE_GAMMA; } } if(fmt == SPNG_FMT_RGBA8) { bytes_per_pixel = 4; max_diff = 256 / diff_div; } else if(fmt == SPNG_FMT_RGBA16) { bytes_per_pixel = 8; max_diff = 65536 / diff_div; } else if(fmt == SPNG_FMT_RGB8) { bytes_per_pixel = 3; have_alpha = 0; max_diff = 256 / diff_div; } else if(fmt == SPNG_FMT_RGB16) { bytes_per_pixel = 6; have_alpha = 0; max_diff = 65536 / diff_div; } else if(fmt == SPNG_FMT_GA8) { bytes_per_pixel = 2; have_alpha = 1; max_diff = 256 / diff_div; } else if(fmt == SPNG_FMT_G8) { bytes_per_pixel = 1; have_alpha = 0; max_diff = 256 / diff_div; } for(y=0; y < h; y++) { for(x=0; x < w; x++) { if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW) && ihdr->bit_depth < 8) px_ofs = (y * row_width) + x / samples_per_byte; else px_ofs = (x + (y * w)) * bytes_per_pixel; if(fmt & (SPNG_FMT_RGBA16 | SPNG_FMT_RGB16)) { uint16_t s_red, s_green, s_blue, s_alpha; uint16_t p_red, p_green, p_blue, p_alpha; memcpy(&s_red, img_spng + px_ofs, 2); memcpy(&s_green, img_spng + px_ofs + 2, 2); memcpy(&s_blue, img_spng + px_ofs + 4, 2); memcpy(&p_red, img_png + px_ofs, 2); memcpy(&p_green, img_png + px_ofs + 2, 2); memcpy(&p_blue, img_png + px_ofs + 4, 2); if(have_alpha) { memcpy(&s_alpha, img_spng + px_ofs + 6, 2); memcpy(&p_alpha, img_png + px_ofs + 6, 2); spng_alpha = s_alpha; png_alpha = p_alpha; } spng_red = s_red; spng_green = s_green; spng_blue = s_blue; png_red = p_red; png_green = p_green; png_blue = p_blue; } else if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) { uint8_t s_red, s_green, s_blue, s_alpha; uint8_t p_red, p_green, p_blue, p_alpha; memcpy(&s_red, img_spng + px_ofs, 1); memcpy(&s_green, img_spng + px_ofs + 1, 1); memcpy(&s_blue, img_spng + px_ofs + 2, 1); memcpy(&p_red, img_png + px_ofs, 1); memcpy(&p_green, img_png + px_ofs + 1, 1); memcpy(&p_blue, img_png + px_ofs + 2, 1); if(have_alpha) { memcpy(&s_alpha, img_spng + px_ofs + 3, 1); memcpy(&p_alpha, img_png + px_ofs + 3, 1); spng_alpha = s_alpha; png_alpha = p_alpha; } spng_red = s_red; spng_green = s_green; spng_blue = s_blue; png_red = p_red; png_green = p_green; png_blue = p_blue; } else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW | SPNG_FMT_G8 | SPNG_FMT_GA8 | SPNG_FMT_GA16)) { if(ihdr->bit_depth <= 8) /* gray 1-8, gray-alpha 8, indexed 1-8 */ { uint8_t s_alpha, s_sample; uint8_t p_alpha, p_sample; memcpy(&s_sample, img_spng + px_ofs, 1); memcpy(&p_sample, img_png + px_ofs, 1); if(shift_amount > 7) shift_amount = initial_shift; s_sample = (s_sample >> shift_amount) & mask; p_sample = (p_sample >> shift_amount) & mask; shift_amount -= ihdr->bit_depth; spng_sample = s_sample; png_sample = p_sample; if(have_alpha) { memcpy(&s_alpha, img_spng + px_ofs + 1, 1); memcpy(&p_alpha, img_png + px_ofs + 1, 1); spng_alpha = s_alpha; png_alpha = p_alpha; } } else /* gray 16, gray-alpha 16 */ { uint16_t s_alpha, s_sample; uint16_t p_alpha, p_sample; memcpy(&s_sample, img_spng + px_ofs, 2); memcpy(&p_sample, img_png + px_ofs, 2); spng_sample = s_sample; png_sample = p_sample; if(have_alpha) { memcpy(&s_alpha, img_spng + px_ofs + 2, 2); memcpy(&p_alpha, img_png + px_ofs + 2, 2); spng_alpha = s_alpha; png_alpha = p_alpha; } } } if(spng_red != png_red || spng_green != png_green || spng_blue != png_blue || spng_sample != png_sample) { if(flags & SPNG_DECODE_GAMMA) { red_diff = abs(spng_red - png_red); green_diff = abs(spng_green - png_green); blue_diff = abs(spng_blue - png_blue); sample_diff = abs(spng_sample - png_sample); if(red_diff > max_diff || green_diff > max_diff || blue_diff > max_diff) { printf("invalid gamma correction at x: %" PRIu32 " y: %" PRIu32 ", " "spng: %" PRIu16 " %" PRIu16 " %" PRIu16 " " "png: %" PRIu16 " %" PRIu16 " %" PRIu16 "\n", x, y, spng_red, spng_green, spng_blue, png_red, png_green, png_blue); pixel_diff = 1; } else if(sample_diff > max_diff) { printf("invalid gamma correction at x: %" PRIu32 " y: %" PRIu32 ", " "spng: %" PRIu16 " png: %" PRIu16 "\n", x, y, spng_sample, png_sample); pixel_diff = 1; } } else { if(spng_sample != png_sample) { char *issue_str = ""; if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) issue_str = "index mismatch"; else issue_str = "grayscale difference"; printf("%s at x: %u y: %u spng: %u png: %u\n", issue_str, x, y, spng_sample, png_sample); } else { printf("color difference at x: %" PRIu32 " y: %" PRIu32 ", " "spng: %" PRIu16 " %" PRIu16 " %" PRIu16 " " "png: %" PRIu16 " %" PRIu16 " %" PRIu16 "\n", x, y, spng_red, spng_green, spng_blue, png_red, png_green, png_blue); } pixel_diff = 1; } } if(have_alpha && spng_alpha != png_alpha) { printf("alpha mismatch at x:%" PRIu32 " y:%" PRIu32 ", " "spng: %" PRIu16 " png: %" PRIu16 "\n", x, y, spng_alpha, png_alpha); alpha_mismatch = 1; } } } if(alpha_mismatch || pixel_diff) return 1; return 0; } static void print_chunks(spngt_chunk_bitfield chunks) { spngt_chunk_bitfield none = { 0 }; if(!memcmp(&none, &chunks, sizeof(spngt_chunk_bitfield))) printf(" (none)"); if(chunks.plte) printf(" PLTE"); if(chunks.trns) printf(" tRNS"); if(chunks.chrm) printf(" cHRM"); if(chunks.gama) printf(" gAMA"); if(chunks.iccp) printf(" iCCP"); if(chunks.sbit) printf(" sBIT"); if(chunks.srgb) printf(" sRGB"); if(chunks.text) printf(" tEXt"); if(chunks.ztxt) printf(" zTXt"); if(chunks.itxt) printf(" iTXt"); if(chunks.bkgd) printf(" bKGD"); if(chunks.hist) printf(" hIST"); if(chunks.phys) printf(" pHYs"); if(chunks.splt) printf(" sPLT"); if(chunks.time) printf(" tIME"); if(chunks.offs) printf(" oFFs"); if(chunks.exif) printf(" eXIF"); if(chunks.unknown) printf(" (unknown)"); } static void free_chunks(spngt_chunk_data *spng) { free(spng->splt); free(spng->text); free(spng->chunks); spng->splt = NULL; spng->text = NULL; spng->chunks = NULL; } static int get_chunks(spng_ctx *ctx, spngt_chunk_data *spng) { int ret = spng_get_ihdr(ctx, &spng->ihdr); if(!ret) spng->have.ihdr = 1; else return ret; ret = spng_get_plte(ctx, &spng->plte); if(!ret) { spng->n_plte_entries = spng->plte.n_entries; spng->have.plte = 1; } else if(ret == SPNG_ECHUNKAVAIL) ret = 0; else return ret; if(!spng_get_trns(ctx, &spng->trns)) spng->have.trns = 1; if(!spng_get_chrm(ctx, &spng->chrm)) spng->have.chrm = 1; if(spng->have.chrm) spng_get_chrm_int(ctx, &spng->chrm_int); if(!spng_get_gama(ctx, &spng->gamma)) spng->have.gama = 1; if(spng->have.gama) spng_get_gama_int(ctx, &spng->gamma_int); if(!spng_get_iccp(ctx, &spng->iccp)) spng->have.iccp = 1; if(!spng_get_sbit(ctx, &spng->sbit)) spng->have.sbit = 1; if(!spng_get_srgb(ctx, &spng->srgb_rendering_intent)) spng->have.srgb = 1; if(!spng_get_text(ctx, NULL, &spng->n_text)) spng->have.text = 1; if(!spng_get_bkgd(ctx, &spng->bkgd)) spng->have.bkgd = 1; if(!spng_get_hist(ctx, &spng->hist)) spng->have.hist = 1; if(!spng_get_phys(ctx, &spng->phys)) spng->have.phys = 1; if(!spng_get_splt(ctx, NULL, &spng->n_splt)) spng->have.splt = 1; if(!spng_get_time(ctx, &spng->time)) spng->have.time = 1; if(!spng_get_offs(ctx, &spng->offs)) spng->have.offs = 1; if(!spng_get_exif(ctx, &spng->exif)) spng->have.exif = 1; if(!spng_get_unknown_chunks(ctx, NULL, &spng->n_unknown_chunks)) spng->have.unknown = 1; if(spng->have.text) { spng->text = malloc(spng->n_text * sizeof(struct spng_text)); if(!spng->text) return 2; spng_get_text(ctx, spng->text, &spng->n_text); } if(spng->have.splt) { spng->splt = malloc(spng->n_splt * sizeof(struct spng_splt)); if(!spng->splt) { free_chunks(spng); return 2; } spng_get_splt(ctx, spng->splt, &spng->n_splt); } if(spng->have.unknown) { spng->chunks = malloc(spng->n_unknown_chunks * sizeof(struct spng_unknown_chunk)); if(!spng->chunks) { free_chunks(spng); return 2; } spng_get_unknown_chunks(ctx, spng->chunks, &spng->n_unknown_chunks); } return ret; } static int set_chunks(spng_ctx *dst, spngt_chunk_data *spng) { int ret = 0; ret = spng_set_ihdr(dst, &spng->ihdr); if(ret) { printf("spng_set_ihdr() error: %s\n", spng_strerror(ret)); return ret; } if(spng->have.plte) spng_set_plte(dst, &spng->plte); if(spng->have.trns) spng_set_trns(dst, &spng->trns); if(spng->have.chrm) spng_set_chrm_int(dst, &spng->chrm_int); if(spng->have.gama) spng_set_gama_int(dst, spng->gamma_int); if(spng->have.iccp) spng_set_iccp(dst, &spng->iccp); if(spng->have.sbit) spng_set_sbit(dst, &spng->sbit); if(spng->have.srgb) spng_set_srgb(dst, spng->srgb_rendering_intent); if(spng->have.text) spng_set_text(dst, spng->text, spng->n_text); if(spng->have.bkgd) spng_set_bkgd(dst, &spng->bkgd); if(spng->have.hist) spng_set_hist(dst, &spng->hist); if(spng->have.phys) spng_set_phys(dst, &spng->phys); if(spng->have.splt) spng_set_splt(dst, spng->splt, spng->n_splt); if(spng->have.time) spng_set_time(dst, &spng->time); if(spng->have.offs) spng_set_offs(dst, &spng->offs); if(spng->have.exif) spng_set_exif(dst, &spng->exif); if(spng->have.unknown) spng_set_unknown_chunks(dst, spng->chunks, spng->n_unknown_chunks); return ret; } static int compare_chunks(spng_ctx *ctx, png_infop info_ptr, png_structp png_ptr, int after_idat) { uint32_t i; enum spng_errno ret = 0; spngt_chunk_data spng = {0}; spngt_chunk_data png = {0}; ret = get_chunks(ctx, &spng); if(ret) goto cleanup; png.have.ihdr = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) png.have.plte = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png.have.trns = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) png.have.chrm = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) png.have.gama = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) png.have.iccp = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) png.have.sbit = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) png.have.srgb = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) png.have.bkgd = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_hIST)) png.have.hist = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) png.have.phys = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tIME)) png.have.time = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_oFFs)) png.have.offs = 1; if(png_get_valid(png_ptr, info_ptr, PNG_INFO_eXIf)) png.have.exif = 1; png.have.unknown = spng.have.unknown; int png_num_palette; png_color *png_palette; if(png_get_PLTE(png_ptr, info_ptr, &png_palette, &png_num_palette) == PNG_INFO_PLTE) png.n_plte_entries = png_num_palette; png_text *png_text; png.n_text = png_get_text(png_ptr, info_ptr, &png_text, NULL); if(png.n_text) png.have.text = 1; png_sPLT_t *png_splt; png.n_splt = png_get_sPLT(png_ptr, info_ptr, &png_splt); if(png.n_splt) png.have.splt = 1; png_unknown_chunk *png_chunks; png.n_unknown_chunks = png_get_unknown_chunks(png_ptr, info_ptr, &png_chunks); if(png.n_unknown_chunks) png.have.unknown = 1; const char *pos = after_idat ? " after IDAT" : "before IDAT"; printf("[%s] spng chunks: ", pos); print_chunks(spng.have); printf("\n"); printf("[%s] libpng chunks:", pos); print_chunks(png.have); printf("\n"); if(memcmp(&spng.have, &png.have, sizeof(spngt_chunk_bitfield))) { printf("[%s] ERROR: metadata mismatch!\n", pos); return 1; } /* NOTE: libpng changes or corrupts chunk data once it's past the IDAT stream, some checks are not done because of this. */ uint32_t png_width, png_height; int png_bit_depth = 0, png_color_type = 0, png_interlace_method = 0, png_compression_method = 0, png_filter_method = 0; png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &png_bit_depth, &png_color_type, &png_interlace_method, &png_compression_method, &png_filter_method); if(spng.ihdr.width != png_width || spng.ihdr.height != png_height || spng.ihdr.bit_depth != png_bit_depth || spng.ihdr.color_type != png_color_type || spng.ihdr.interlace_method != png_interlace_method || spng.ihdr.compression_method != png_compression_method || spng.ihdr.filter_method != png_filter_method) { if(!after_idat) { printf("IHDR data not identical\n"); ret = 1; goto cleanup; } } if(spng.plte.n_entries != png.n_plte_entries) { printf("different number of palette entries (%u, %u)\n", spng.plte.n_entries, png.n_plte_entries); ret = 1; } else { for(i=0; i < spng.plte.n_entries; i++) { if(spng.plte.entries[i].red != png_palette[i].red || spng.plte.entries[i].green != png_palette[i].green || spng.plte.entries[i].blue != png_palette[i].blue) { printf("palette entry %d not identical\n", i); ret = 1; } } } if(spng.have.trns) { png_byte *png_trans_alpha; int png_num_trans; png_color_16 *png_trans_color; png_get_tRNS(png_ptr, info_ptr, &png_trans_alpha, &png_num_trans, &png_trans_color); if(spng.ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) { if(spng.trns.gray != png_trans_color->gray) { printf("tRNS gray sample is not identical\n"); ret = 1; } } else if(spng.ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) { if(spng.trns.red != png_trans_color->red || spng.trns.green != png_trans_color->green || spng.trns.blue != png_trans_color->blue) { printf("tRNS truecolor samples not identical\n"); ret = 1; } } else if(spng.ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) { if(spng.trns.n_type3_entries == png_num_trans) { for(i=0; i < spng.trns.n_type3_entries; i++) { if(spng.trns.type3_alpha[i] != png_trans_alpha[i]) { printf("tRNS alpha entry %d not identical\n", i); ret = 1; } } } else { if(!after_idat) { printf("different number of tRNS alpha entries\n"); ret = 1; } } } } if(spng.have.chrm) { png_fixed_point png_fwhite_x, png_fwhite_y, png_fred_x, png_fred_y, png_fgreen_x, png_fgreen_y, png_fblue_x, png_fblue_y; png_get_cHRM_fixed(png_ptr, info_ptr, &png_fwhite_x, &png_fwhite_y, &png_fred_x, &png_fred_y, &png_fgreen_x, &png_fgreen_y, &png_fblue_x, &png_fblue_y); if(spng.chrm_int.white_point_x != png_fwhite_x || spng.chrm_int.white_point_y != png_fwhite_y || spng.chrm_int.red_x != png_fred_x || spng.chrm_int.red_y != png_fred_y || spng.chrm_int.green_x != png_fgreen_x || spng.chrm_int.green_y != png_fgreen_y || spng.chrm_int.blue_x != png_fblue_x || spng.chrm_int.blue_y != png_fblue_y) { printf("cHRM fixed point values are not identical\n"); ret = 1; } } if(spng.have.gama) { png_fixed_point png_fgamna; png_get_gAMA_fixed(png_ptr, info_ptr, &png_fgamna); if(spng.gamma_int != png_fgamna) { printf("gamma values not identical\n"); ret = 1; } } if(spng.have.iccp) { png_charp png_iccp_name; int png_iccp_compression_type; png_bytep png_iccp_profile; png_uint_32 png_iccp_proflen; png_get_iCCP(png_ptr, info_ptr, &png_iccp_name, &png_iccp_compression_type, &png_iccp_profile, &png_iccp_proflen); if(spng.iccp.profile_len == png_iccp_proflen) { if(memcmp(spng.iccp.profile, png_iccp_profile, spng.iccp.profile_len)) { printf("iccp profile data not identical\n"); ret = 1; } } else { printf("iccp profile lengths are different\n"); ret = 1; } } if(spng.have.sbit) { png_color_8p png_sig_bit; png_get_sBIT(png_ptr, info_ptr, &png_sig_bit); if(spng.ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE && spng.sbit.grayscale_bits != png_sig_bit->gray) { printf("grayscale significant bits not identical\n"); ret = 1; } else if(spng.ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR || spng.ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) { if(spng.sbit.red_bits != png_sig_bit->red || spng.sbit.green_bits != png_sig_bit->green || spng.sbit.blue_bits != png_sig_bit->blue) { printf("rgb significant bits not identical\n"); ret = 1; } } else if(spng.ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) { if(spng.sbit.grayscale_bits != png_sig_bit->gray || spng.sbit.alpha_bits != png_sig_bit->alpha) { printf("grayscale alpha significant bits not identical\n"); ret = 1; } } else if(spng.ihdr.color_type == 6) { if(spng.sbit.red_bits != png_sig_bit->red || spng.sbit.green_bits != png_sig_bit->green || spng.sbit.blue_bits != png_sig_bit->blue || spng.sbit.alpha_bits != png_sig_bit->alpha) { printf("rgba significant bits not identical\n"); ret = 1; } } } if(spng.have.srgb) { int png_rgb_intent; png_get_sRGB(png_ptr, info_ptr, &png_rgb_intent); if(spng.srgb_rendering_intent != png_rgb_intent) { printf("sRGB rendering intent mismatch\n"); ret = 1; } } if(spng.n_text != png.n_text) { printf("text chunk count mismatch: %u(spng), %d(libpng)\n", spng.n_text, png.n_text); ret = 1; goto cleanup; } else { for(i=0; i < spng.n_text; i++) { if(strcmp(spng.text[i].keyword, png_text[i].key)) { printf("text[%d]: keyword mismatch!\nspng: %s\n\nlibpng: %s\n", i, spng.text[i].keyword, png_text[i].key); ret = 1; } if(strcmp(spng.text[i].text, png_text[i].text)) { printf("text[%d]: text mismatch!\nspng: %s\n\nlibpng: %s\n", i, spng.text[i].text, png_text[i].text); ret = 1; } if(spng.text[i].type != SPNG_ITXT) continue; if(strcmp(spng.text[i].language_tag, png_text[i].lang)) { printf("text[%d]: language tag mismatch!\nspng: %s\n\nlibpng: %s\n", i, spng.text[i].language_tag, png_text[i].lang); ret = 1; } if(strcmp(spng.text[i].translated_keyword, png_text[i].lang_key)) { printf("text[%d]: translated keyword mismatch!\nspng: %s\n\nlibpng: %s\n", i, spng.text[i].translated_keyword, png_text[i].lang_key); ret = 1; } } } if(spng.have.bkgd) { png_color_16p png_bkgd; png_get_bKGD(png_ptr, info_ptr, &png_bkgd); if(spng.ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE || spng.ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) { if(spng.bkgd.gray != png_bkgd->gray) { printf("bKGD grayscale samples are not identical\n"); ret = 1; } } else if(spng.ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR || spng.ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) { if(spng.bkgd.red != png_bkgd->red || spng.bkgd.green != png_bkgd->green || spng.bkgd.blue != png_bkgd->blue) { printf("bKGD rgb samples are not identical\n"); ret = 1; } } else if(spng.ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) { if(spng.bkgd.plte_index != png_bkgd->index) { printf("bKGD type3 indices are not identical\n"); ret = 1; } } } if(spng.have.hist) { png_uint_16p png_hist; png_get_hIST(png_ptr, info_ptr, &png_hist); for(i=0; i < spng.plte.n_entries; i++) { if(spng.hist.frequency[i] != png_hist[i]) { printf("histogram entry %d is not identical\n", i); ret = 1; } } } if(spng.have.phys) { uint32_t png_phys_res_x, png_phys_rex_y; int png_phys_unit_type; png_get_pHYs(png_ptr, info_ptr, &png_phys_res_x, &png_phys_rex_y, &png_phys_unit_type); if(spng.phys.ppu_x != png_phys_res_x || spng.phys.ppu_y != png_phys_rex_y || spng.phys.unit_specifier != png_phys_unit_type) { printf("pHYs data not indentical\n"); ret = 1; } } if(spng.n_splt != png.n_splt) { printf("different number of suggested palettes\n"); ret = 1; } else { uint32_t j; for(j=0; j < spng.n_splt; j++) { if(strcmp(spng.splt[j].name, png_splt[j].name)) { printf("sPLT[%d]: name mismatch\n", j); ret = 1; } if(spng.splt[j].sample_depth != png_splt[j].depth) { printf("sPLT[%d]: sample depth mismatch\n", j); ret = 1; } if(spng.splt[j].n_entries != png_splt[j].nentries) { printf("sPLT[%d]: entry count mismatch\n", j); ret = 1; break; } struct spng_splt_entry entry; png_sPLT_entry png_entry; for(i=0; i < spng.splt[j].n_entries; i++) { entry = spng.splt[j].entries[i]; png_entry = png_splt[j].entries[i]; if(entry.alpha != png_entry.alpha || entry.red != png_entry.red || entry.green != png_entry.green || entry.blue != png_entry.blue || entry.frequency != png_entry.frequency) { printf("sPLT[%d]: mismatch for entry %d\n", j, i); ret = 1; } } } } if(spng.have.time) { png_time *png_time; png_get_tIME(png_ptr, info_ptr, &png_time); if(spng.time.year != png_time->year || spng.time.month != png_time->month || spng.time.day != png_time->day || spng.time.hour != png_time->hour || spng.time.minute != png_time->minute || spng.time.second != png_time->second) { printf("tIME data not identical\n"); ret = 1; } } if(spng.have.offs) { png_int_32 png_offset_x, png_offset_y; int png_offs_unit_type; png_get_oFFs(png_ptr, info_ptr, &png_offset_x, &png_offset_y, &png_offs_unit_type); if(spng.offs.x != png_offset_x || spng.offs.y != png_offset_y || spng.offs.unit_specifier != png_offs_unit_type) { printf("oFFs data not identical\n"); ret = 1; } } if(spng.have.exif) { png_byte *png_exif; png_uint_32 png_exif_length; png_get_eXIf_1(png_ptr, info_ptr, &png_exif_length, &png_exif); if(spng.exif.length == png_exif_length) { if(memcmp(spng.exif.data, png_exif, spng.exif.length)) { printf("eXIf data not identical\n"); ret = 1; } } else { printf("eXIf chunk length mismatch\n"); ret = 1; } } if(png.n_unknown_chunks != spng.n_unknown_chunks) { printf("unknown chunk count mismatch: %u(spng), %d(libpng)\n", spng.n_unknown_chunks, png.n_unknown_chunks); ret = 1; goto cleanup; } else { for(i=0; i < spng.n_unknown_chunks; i++) { if(spng.chunks[i].length != png_chunks[i].size) { printf("chunk[%d]: size mismatch %zu (spng) %zu (libpng)\n", i, spng.chunks[i].length, png_chunks[i].size); ret = 1; } if(spng.chunks[i].location != png_chunks[i].location) { printf("chunk[%d]: location mismatch\n", i); ret = 1; } } } cleanup: free_chunks(&spng); return ret; } static int decode_and_compare(spngt_test_case *spng, spngt_test_case *png) { int ret = 0; spng_ctx *ctx = NULL; size_t img_spng_size; unsigned char *img_spng = NULL; int fmt = spng->fmt, flags = spng->flags, test_flags = spng->test_flags; png_infop info_ptr = NULL; png_structp png_ptr = NULL; size_t img_png_size; unsigned char *img_png = NULL; struct spng_ihdr ihdr; ctx = init_spng(spng, &ihdr); if(ctx == NULL) { ret = 1; goto cleanup; } spng_set_crc_action(ctx, SPNG_CRC_ERROR, SPNG_CRC_ERROR); img_spng = getimage_spng(ctx, &img_spng_size, fmt, flags); if(img_spng == NULL) { printf("getimage_spng() failed\n"); ret = 1; goto cleanup; } png_ptr = init_libpng(png, &info_ptr); if(png_ptr == NULL) { ret = 1; goto cleanup; } if(test_flags & SPNGT_COMPARE_CHUNKS) { ret = compare_chunks(ctx, info_ptr, png_ptr, 0); } img_png = getimage_libpng(png_ptr, info_ptr, &img_png_size, fmt, flags); if(img_png == NULL) { printf("getimage_libpng() failed\n"); ret = 1; goto cleanup; } if(img_png_size != img_spng_size) { printf("output image size mismatch\n"); printf("spng: %lu\n png: %lu\n", (unsigned long int)img_spng_size, (unsigned long int)img_png_size); ret = 1; goto cleanup; } if(fmt == SPNGT_FMT_VIPS) {/* Get the right format for compare_images() */ fmt = SPNG_FMT_PNG; if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) fmt = SPNG_FMT_RGB8; else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr.bit_depth < 8) fmt = SPNG_FMT_G8; spng_get_trns_fmt(ctx, &fmt); printf("VIPS format: %s\n", fmt_str(fmt)); } if(!memcmp(img_spng, img_png, img_spng_size)) goto identical; if( !(flags & SPNG_DECODE_GAMMA) ) { printf("error: image buffers are not identical\n"); ret = 1; } ret |= compare_images(&ihdr, fmt, flags, img_spng, img_png, img_spng_size); if(!ret && !(flags & SPNG_DECODE_GAMMA)) {/* in case compare_images() has some edge case */ printf("compare_images() returned 0 but images are not identical\n"); ret = 1; goto cleanup; } identical: if(test_flags & SPNGT_COMPARE_CHUNKS) { ret |= compare_chunks(ctx, info_ptr, png_ptr, 1); } cleanup: spng_ctx_free(ctx); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(img_spng); free(img_png); return ret; } static void dump_buffer(void *buf, size_t size, const char *opt_name) { const char *filename = opt_name ? opt_name : "dump.png"; FILE *f = fopen(filename, "wb"); if(f == NULL) goto err; size_t written = fwrite(buf, size, 1, f); if(written != 1) goto err; printf("file dumped to %s\n", filename); goto cleanup; err: printf("failed to dump file: %s\n", strerror(errno)); cleanup: if(f != NULL) fclose(f); } static int spngt_run_test(const char *filename, struct spngt_test_case *test_case) { enum spng_errno ret; size_t file_length = 0; struct spngt_test_case spng = *test_case; struct spngt_test_case libpng = *test_case; if(test_case->source.type == SPNGT_SRC_FILE) { spng.source.file = fopen(filename, "rb"); libpng.source.file = fopen(filename, "rb"); if(!spng.source.file || !libpng.source.file) { ret = 1; goto cleanup; } fseek(spng.source.file, 0, SEEK_END); file_length = ftell(spng.source.file); rewind(spng.source.file); } ret = decode_and_compare(&spng, &libpng); if(ret) goto cleanup; if(test_case->test_flags & SPNGT_ENCODE_ROUNDTRIP) { printf(" compare (reencoded, original)...\n"); if(test_case->source.type == SPNGT_SRC_FILE) { rewind(spng.source.file); rewind(libpng.source.file); } spng_ctx *src = init_spng(&spng, NULL); spng_ctx *dst = NULL; spngt_chunk_data data = {0}; size_t img_size; void *img_spng = getimage_spng(src, &img_size, test_case->fmt, test_case->flags); size_t encoded_len; char *encoded_pngbuf = NULL; if(img_spng == NULL) { ret = 1; goto encode_cleanup; } ret = get_chunks(src, &data); if(ret) goto encode_cleanup; dst = spng_ctx_new(SPNG_CTX_ENCODER); spng_set_option(dst, SPNG_ENCODE_TO_BUFFER, 1); ret = set_chunks(dst, &data); if(ret) goto encode_cleanup; ret = spng_encode_image(dst, img_spng, img_size, test_case->fmt, SPNG_ENCODE_FINALIZE); if(ret) { printf("encode error: %s\n", spng_strerror(ret)); goto encode_cleanup; } encoded_pngbuf = spng_get_png_buffer(dst, &encoded_len, &ret); if(ret) { printf("failed to get encoded PNG: %s\n", spng_strerror(ret)); goto encode_cleanup; } /* Unfortunately there's a handful of testsuite image that don't compress well with the default filter heuristic */ /* Fail the test on a 4% size increase */ /*int pct = 25; if( (encoded_len - encoded_len / pct) > file_length) { printf("Reencoded PNG exceeds maximum %d%% size increase: %zu (original: %zu)\n", 100 / pct, encoded_len, file_length); ret = 1; goto encode_cleanup; }*/ (void)file_length; spng.source.type = SPNGT_SRC_BUFFER; spng.source.buffer = encoded_pngbuf; spng.source.png_size = encoded_len; spng.test_flags |= SPNGT_COMPARE_CHUNKS; ret = decode_and_compare(&spng, &libpng); if(ret) { printf("compare error (%d))\n", ret); dump_buffer(encoded_pngbuf, encoded_len, NULL); goto encode_cleanup; } libpng.source.type = SPNGT_SRC_BUFFER; libpng.source.buffer = encoded_pngbuf; libpng.source.png_size = encoded_len; libpng.test_flags |= SPNGT_COMPARE_CHUNKS; printf(" compare (reencoded, reencoded)...\n"); ret = decode_and_compare(&spng, &libpng); if(ret) { printf("compare error (%d))\n", ret); dump_buffer(encoded_pngbuf, encoded_len, NULL); } encode_cleanup: free(img_spng); free(encoded_pngbuf); spng_ctx_free(src); spng_ctx_free(dst); free_chunks(&data); } if(test_case->test_flags & SPNGT_EXTENDED_TESTS) { rewind(spng.source.file); ret = extended_tests(spng.source.file, test_case->fmt); if(ret) printf("extended tests failed\n"); } cleanup: if(spng.source.file) fclose(spng.source.file); if(libpng.source.file) fclose(libpng.source.file); return ret; } static int get_image_info(FILE *f, struct spng_ihdr *ihdr) { spng_ctx *ctx = spng_ctx_new(0); spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE); spng_set_png_file(ctx, f); int ret = spng_get_ihdr(ctx, ihdr); spng_ctx_free(ctx); return ret; } int main(int argc, char **argv) { if(argc < 2) { printf("no input file\n"); return 1; } char *filename = argv[1]; if(!strcmp(filename, "info")) { unsigned int png_ver = png_access_version_number(); printf("spng header version: %u.%u.%u, library version: %s\n", SPNG_VERSION_MAJOR, SPNG_VERSION_MINOR, SPNG_VERSION_PATCH, spng_version_string()); printf("png header version: %u.%u.%u, library version: %u.%u.%u\n", PNG_LIBPNG_VER_MAJOR, PNG_LIBPNG_VER_MINOR, PNG_LIBPNG_VER_RELEASE, png_ver / 10000, png_ver / 100 % 100, png_ver % 100); return 0; } FILE *file = fopen(filename, "rb"); if(file == NULL) { printf("error opening input file %s\n", filename); return 1; } struct spng_ihdr ihdr = {0}; if(!get_image_info(file, &ihdr)) { char *clr_type_str; if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) clr_type_str = "GRAY"; else if(ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) clr_type_str = "RGB"; else if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) clr_type_str = "INDEXED"; else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) clr_type_str = "GRAY-ALPHA"; else clr_type_str = "RGBA"; printf("%s %" PRIu8 "-bit, %" PRIu32 "x%" PRIu32 " %s\n", clr_type_str, ihdr.bit_depth, ihdr.width, ihdr.height, ihdr.interlace_method ? "interlaced" : "non-interlaced"); } else printf("failed to get image info\n"); fclose(file); /* With libpng it's not possible to request 8/16-bit images regardless of PNG format without calling functions that alias to png_set_expand(_16), which acts as if png_set_tRNS_to_alpha() was called, as a result there are no tests where transparency is not applied */ int gamma_bug = 0; int skip_encode = 0; int fmt_limit = SPNGT_SKIP; int fmt_limit_2 = SPNGT_SKIP; /* https://github.com/randy408/libspng/issues/17 */ if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) gamma_bug = SPNGT_SKIP; /* Some output formats are limited to certain PNG formats */ if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr.bit_depth <= 8) fmt_limit = 0; if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr.bit_depth == 16) fmt_limit_2 = 0; add_test_case(SPNG_FMT_PNG, 0, SPNGT_COMPARE_CHUNKS); add_test_case(SPNG_FMT_PNG, 0, SPNGT_ENCODE_ROUNDTRIP | skip_encode); add_test_case(SPNG_FMT_RAW, 0, SPNGT_ENCODE_ROUNDTRIP | skip_encode); add_test_case(SPNG_FMT_RAW, 0, 0); add_test_case(SPNG_FMT_RGBA8, SPNG_DECODE_TRNS, 0); add_test_case(SPNG_FMT_RGBA8, SPNG_DECODE_TRNS | SPNG_DECODE_GAMMA, gamma_bug); add_test_case(SPNG_FMT_RGBA16, SPNG_DECODE_TRNS, 0); add_test_case(SPNG_FMT_RGBA16, SPNG_DECODE_TRNS | SPNG_DECODE_GAMMA, 0); add_test_case(SPNG_FMT_RGB8, 0, 0); add_test_case(SPNG_FMT_RGB8, SPNG_DECODE_GAMMA, gamma_bug); add_test_case(SPNG_FMT_G8, 0, fmt_limit); add_test_case(SPNG_FMT_GA8, 0, fmt_limit); add_test_case(SPNG_FMT_GA8, SPNG_DECODE_TRNS, fmt_limit); add_test_case(SPNG_FMT_GA16, 0, fmt_limit_2); add_test_case(SPNG_FMT_GA16, SPNG_DECODE_TRNS, fmt_limit_2); /* This tests the input->output format logic used in libvips, it emulates the behavior of their old PNG loader which uses libpng. */ add_test_case(SPNGT_FMT_VIPS, SPNG_DECODE_TRNS, 0); add_test_case(SPNG_FMT_PNG, 0, SPNGT_EXTENDED_TESTS); printf("%d test cases", n_test_cases); if(n_test_cases != actual_count) printf(" (skipping %d)", n_test_cases - actual_count); printf("\n"); int i, ret = 0; for(i=0; i < n_test_cases; i++) { print_test_args(&test_cases[i]); if(test_cases[i].test_flags & SPNGT_SKIP) continue; int e = spngt_run_test(filename, &test_cases[i]); if(!ret) ret = e; } return ret; } static int stream_write_checked(spng_ctx *ctx, void *user, void *data, size_t len) { (void)ctx; struct buf_state *state = user; if(len > state->bytes_left) return SPNG_IO_EOF; if(memcmp(data, state->data, len)) { printf("stream write does not match buffer data\n"); return SPNG_IO_ERROR; } state->data += len; state->bytes_left -= len; return 0; } /* Tests that don't fit anywhere else */ static int extended_tests(FILE *file, int fmt) { uint32_t i; enum spng_errno ret = 0; unsigned char *image = NULL; unsigned char *encoded = NULL; spng_ctx *enc = NULL; spng_ctx *dec = spng_ctx_new(0); struct spng_ihdr ihdr = {0}; struct spng_plte plte = {0}; static unsigned char chunk_data[9000]; /* NOTE: This value is compressed to 2 bits by zlib, it's not a 1:1 mapping */ int compression_level = 0; int expected_compression_level = 0; spng_set_png_file(dec, file); spng_get_ihdr(dec, &ihdr); spng_get_plte(dec, &plte); size_t image_size; image = getimage_spng(dec, &image_size, fmt, 0); enc = spng_ctx_new(SPNG_CTX_ENCODER); spng_set_option(enc, SPNG_ENCODE_TO_BUFFER, 1); spng_set_option(enc, SPNG_IMG_COMPRESSION_LEVEL, compression_level); spng_set_ihdr(enc, &ihdr); if(plte.n_entries) spng_set_plte(enc, &plte); struct spng_unknown_chunk chunk = { .location = SPNG_AFTER_IHDR, .type = "cHNK", .length = sizeof(chunk_data), .data = chunk_data }; uint8_t x = 0; for(i=0; i < chunk.length; i++) { chunk_data[i] = x; x++; } spng_set_unknown_chunks(enc, &chunk, 1); ret = spng_encode_image(enc, image, image_size, fmt, SPNG_ENCODE_FINALIZE); if(ret) { printf("encoding failed (%d): %s\n", ret, spng_strerror(ret)); goto cleanup; } size_t bytes_encoded; encoded = spng_get_png_buffer(enc, &bytes_encoded, &ret); if(!encoded) { printf("getting buffer failed (%d): %s\n", ret, spng_strerror(ret)); goto cleanup; } spng_ctx_free(enc); enc = NULL; /* Verify the image's zlib FLEVEL */ spng_ctx_free(dec); dec = spng_ctx_new(0); spng_set_png_buffer(dec, encoded, bytes_encoded); spng_decode_image(dec, NULL, 0, SPNG_FMT_PNG, SPNG_DECODE_PROGRESSIVE); ret = spng_get_option(dec, SPNG_IMG_COMPRESSION_LEVEL, &compression_level); if(ret || (compression_level != expected_compression_level) ) { if(ret) printf("error getting image compression level: %s\n", spng_strerror(ret)); else { printf("unexpected compression level (expected %d, got %d)\n", expected_compression_level, compression_level); ret = 1; } goto cleanup; } /* Reencode the same image but to a stream this time */ enc = spng_ctx_new(SPNG_CTX_ENCODER); struct buf_state state = { .data = encoded, .bytes_left = bytes_encoded }; spng_set_png_stream(enc, stream_write_checked, &state); spng_set_option(enc, SPNG_IMG_COMPRESSION_LEVEL, compression_level); spng_set_ihdr(enc, &ihdr); if(plte.n_entries) spng_set_plte(enc, &plte); spng_set_unknown_chunks(enc, &chunk, 1); ret = spng_encode_image(enc, 0, 0, fmt, SPNG_ENCODE_PROGRESSIVE | SPNG_ENCODE_FINALIZE); if(ret) { printf("progressive init failed: %s\n", spng_strerror(ret)); goto cleanup; } struct spng_row_info row_info = {0}; size_t image_width = image_size / ihdr.height; do { ret = spng_get_row_info(enc, &row_info); if(ret) break; void *ptr = image + image_width * row_info.row_num; ret = spng_encode_row(enc, ptr, image_width); }while(!ret); if(ret == SPNG_EOI) ret = 0; if(ret) { printf("reencode to stream failed (%d): %s\n", ret, spng_strerror(ret)); printf("stream offset: %zu\n", bytes_encoded - state.bytes_left); } if(spng_get_png_buffer(enc, &bytes_encoded, &ret)) { printf("this should not happen\n"); ret = 1; goto cleanup; } if(!ret) { printf("spng_get_png_buffer(): invalid return value\n"); ret = 1; goto cleanup; } else ret = 0; /* clear the (expected) error */ if(state.bytes_left) { printf("incomplete stream (%zu bytes shorter)\n", state.bytes_left); ret = 1; } cleanup: free(image); free(encoded); spng_ctx_free(dec); spng_ctx_free(enc); return ret; }